Async Pipe "as" Syntax Is Just The $implicit View Context Property In Angular 7.0.3
When I started to experiment with the Runtime abstraction for state management in Angular 7.0.3, I used the Async Pipe for the first time. The Async Pipe is nice in that it inherently manages RxJS Observable Stream subscriptions for you; but, it ends up producing noisier Angular Template markup. One way to reduce the amount of noise in the template is to save the emitted stream values "as" template-local variables. At first, this "as" syntax seemed magical. But, when I realized that it was using the "$implicit" property of the NgIf directive's embedded view template, the magic faded away and was replaced with fact.
Run this demo in my JavaScript Demos project on GitHub.
View this code in my JavaScript Demos project on GitHub.
When a structural directive is instantiated, it is given a TemplateRef that it can then render inside the ViewContainerRef. As part of this rendering, the directive provides a "context" object for the template. This context object provides the values that can be bound to template-local variables using the "let" operator.
For the NgIf structural directive, the NgIf expression is provided as the "$implicit" key on this context object. So, when you do something like this in an Angular template:
*ngIf="( valueStream | async ) as value"
The values emitted by the "valueStream" object are bound to the "$implicit" content property. The "as value" syntax then binds the "$implicit" context property to a template-local variable called "value".
This becomes more clear when we rework the same NgIf directive using several different but equivalent syntaxes:
// Import the core angular services.
import { Component } from "@angular/core";
import { Observable } from "rxjs";
import { of } from "rxjs";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
styleUrls: [ "./app.component.less" ],
template: `
<div *ngIf="( valueStream | async ) as value">
{{ value }}
</div>
<div *ngIf="( valueStream | async ) ; $implicit as value">
{{ value }}
</div>
<div *ngIf="( valueStream | async ) ; let value = $implicit">
{{ value }}
</div>
<ng-template [ngIf]="( valueStream | async )" let-value>
<div>
{{ value }}
</div>
</ng-template>
<ng-template [ngIf]="( valueStream | async )" let-value="$implicit">
<div>
{{ value }}
</div>
</ng-template>
`
})
export class AppComponent {
public valueStream: Observable<string>;
// I initialize the app component.
constructor() {
this.valueStream = of( "woot !" );
}
}
As you can see, we're using both the "sugar" and "ng-template" syntaxes. We're also using the "as" syntax as well as several forms of the "let" syntax. But, these are all the same. And, when we run this, we get the following browser output:
As you can see, all of these approaches lead to the same outcome because they are all the same thing.
Now, to add one more point of clarity, the Async Pipe has nothing to do with the "as" or "let" syntax. In fact, you can use the "as" syntax to create template-local variables for non-async values:
<div *ngIf=" 'noice' as value ">
{{ value }}
</div>
<ng-template [ngIf]=" 'noice' " let-value="ngIf">
<div>
{{ value }}
</div>
</ng-template>
In this case, we're just re-mapping the NgIf expression value to the template-local variable, "value". And, in the latter case, we're using "ngIf" instead of "$implicit" as these are the same properties (in the NgIf directive).
I know when I first saw the "as" syntax in an NgRx stream-based view, I had two thoughts: first, the "as" syntax was directly related to streams; and second, the "as" syntax was magical. Once I started to dig into how it worked, however, I realized that neither of these assumptions were true. The "as" syntax is neither magical nor does it have anything to do with asynchronous / observable streams. Hopefully this may clarify it for anyone else who was confused (or unknowingly confused).
Want to use code from this post? Check out the license.
Reader Comments
@All,
After digging deeper into
*
template desugaring, I realized that my assumptions here were not always correct:www.bennadel.com/blog/3529-understanding-the-limitations-of-template-syntax-desugaring-in-angular-7-0-4.htm
Above, I states that the
as
syntax is a short-hand binding to the$implicit
context property. But, this is only coincidentally true with theNgIf
directive. More generally, theas
syntax is a short-hand binding to whatever the contextual property is. To see what I mean, take a look at the post I linked above.