Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do Nested Angular2 Async Pipes Resolve?

Tags:

angular

I'm a little confused as to how and when nested async pipes resolve in angular2 templates, and the documentation isn't in a great place right now, so I'm hoping someone on SO can help.

I have a very simple Rxjs Observable returned from a service, via Observable.of(myArray).delay(2000) - the delay is there to help show what is going on with the timing.

In my template, I'm just using an async pipe on the observable returned above in an enclosing <p> tag to control when it is shown, and then trying to show the returned array length inside of that <p> tag:

<p *ngIf="!(lists | async)">Waiting for lists...</p>
<p *ngIf="lists | async">We have lists! How many? => {{(lists | async)?.length}}</p>

View the Plunker.

So when you load this up, "waiting for lists" is displayed, 2 seconds later we get the "We have lists!" part, as expected, but then it takes another 2 seconds for the inner async pipe to resolve and display the resolved array length.

How can I get the length to show up at the same time as everything else that depends on the Observable returning its value(s)? Or is this just not a good use case for async pipes, and I should just subscribe() in my component instead?

like image 473
chucknelson Avatar asked Mar 03 '16 02:03

chucknelson


1 Answers

Async pipes are just fine. There's another thing involved in this subject.

Check at NgIf directive source code.

When the condition is true it embedded the view into the view container.

this._viewContainer.createEmbeddedView(this._templateRef);

Docs for ViewContainerRef#createEmbeddedView

Instantiates an Embedded View based on the templateRef and inserts it into this container at the specified index.

Basically it takes whatever it is inside the NgIf, instantiates it and puts it in the DOM.

When the condition is false it removes everything from the DOM and clears all the views inside of it

this._viewContainer.clear();

Docs for ViewContainerRef#clear

Destroys all Views in this container.

So, now that we know what NgIf does, why are you seeing this behavior? Simple, and I'll explain it in steps

  1. <p *ngIf="!(lists | async)">Waiting for lists...</p> : At this point lists result doesn't yet arrive, so this is executed.

  2. <p *ngIf="lists | async" : This ngIf will be executed in two seconds (the delay time you set for it). Once the value arrives the NgIf directive will instantiate what it is inside and put it in the DOM.

  3. (lists | async)?.length : This async pipe is executed once it has been instatiated, two seconds later than above.

So your timeline it would look like this (I'm really sorry for my poor timeline design)

*ngIf="lists | async" 
----(2 seconds)-----> 

                     (lists | async)?.length 
                     ------(2 seconds)----->   
                                              print value

That's why you see this difference. The *ngIf is not running in parallel with the ?.length.

If you want to see it immediatly you'll have to remove the delay operator, or subscribe manually and set the value yourself like as follows

// Template
<p *ngIf="lists">We have lists! How many? => {{lists.length}} some value</p>

// Observable
this._listService.getLists(2000).subscribe(res => this.lists = res);

This will affect your other async pipes of course. See this plnkr with your code working.

I hope this helps and clarifies a little bit.

like image 65
Eric Martinez Avatar answered Oct 25 '22 00:10

Eric Martinez