3
votes

I try to learn observable programming with Angular 2 BUT I don't get one important point - how to pass in the value that is in an observable in the (click) event in the template level (without subscribing the observable in the TS file and turn the observable value into member variable).

As lots of Firebase experts suggest this "Wherever possible, try to use | async in angular2 and prevent manual subscriptions!" - reason for not wanting to manually subscribe to task$ and turn it into an array.

For example:

I have this in my TS file of component:

task$: Observable<Task>;

this.task$ = this.tasksService.findTaskById(taskId);

In my service, this is the code to call firebase with AngularFire which return an observable:

import {Task} from "../model/task";
constructor(private db:AngularFireDatabase, @Inject(FirebaseRef) fb) {
   this.sdkDb = fb.database().ref();
}

findTaskById(id:string):Observable<Task> {

  return this.db.object('tasks/' + id).map(task => Task.fromJson(task));

}

In my template, I can use the value without problem in Angular2 with async pipe like so:

<md-card-title>{{(task$ | async)?.title}}</md-card-title>

But then I have a button that need the $id value inside this task$ like so:

<button md-button *ngIf="(authInfo$ | async)?.isLoggedIn()" (click)="deleteTask((task$ | async)?.$key)">Delete</button>

This won't work as click does not allow pipe inside the expression... And I DO NOT want to subscribe to task$ and turn it into member variable (best practises to keep it task$ for obserable style RXJS programming?!)

So how do I get the $key value as it is not there initially and I can not use async pipe!?

3
Can you show your service? where tasksService.findTaskById(taskId) is called? - Pezetter
Hi @pezetter ! I added the service call. It is basically an angularFire2 observable. The task$ is working fine. It is (click) not allow async pipe is the issue. I have no idea how to avoid that and not use subscribe. - Hugh Hou
What is the purpose of the async pipe? Since the component defines task, and you are ultimately calling a method of the component (deleteTask), why do you need to pass task$ to deleteTask$? Why not just use it directly in the deleteTask method? - JSF
To get the task$ in the template without using subscribe? This is standard Angular2 reactive programming code... You use async pipe to unwrap your observable? If you use subscribe, you need to manually unsubscribe the observable when it is done, which is better to let async to deal with it in template level... - Hugh Hou
It does make sense however your approach is incorrect. You can access task$ by subscribing to in code (rather than whatever 3rd party library you are using). You must subscribe to task$ using .subscribe(). In the method deleteTask, use: this.task$.subscribe( (value) => { /*your code here*/ }); - JSF

3 Answers

7
votes

For the general case of adding a click handler to anything, not just a <button>, you can use a hidden <input> element to hold the latest value of the promise/observable stream, and then access that value from other elements.

  <input #dummy style="display: none" type="text" value="{{ myAsyncSource | async }}">
  <a (click)="myFunction(dummy.value)">{{ dummy.value }}</a>
2
votes

The way I solved the issue is that i created a variable for the button and passed its value to the handler. Also the Observable is bound to value property.

 <button type="button" #item value="{{i$ | async}}"  (click)="buttonClicked(item.value)">BUTTON</button>
1
votes

You can use "*ngIf as":

<div *ngIf="(task$ | async) as task">
  <button (click)="deleteTask(task.id)">Delete</button>
</div>