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?
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
<p *ngIf="!(lists | async)">Waiting for lists...</p>
: At this point lists
result doesn't yet arrive, so this is executed.
<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.
(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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With