Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Andrew Wirick
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Andrew Wirick@amWirick )

Async Pipe "as" Syntax Is Just The $implicit View Context Property In Angular 7.0.3

By Ben Nadel on

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:


 
 
 

 
 The Async Pipe syntax for  
 
 
 

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).



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

@All,

After digging deeper into * template desugaring, I realized that my assumptions here were not always correct:

https://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 the NgIf directive. More generally, the as 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.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.