Creating a dialog in two lines of code

The theme of our week so far is that we don’t always need to think just in terms of components. We saw that directives and CSS selectors can be powerful tools to keep in mind.

Today, I’m going to show how modern HTML and template reference variables can do a lot of work for us with very little code.

Dialogs and modal windows have been a pain to work with for a very long time on the web. Not anymore though.

Here are a few lines of code that makes it all work painlessly:

dialog is a modern addition to HTML with a simple API: show(), showModal(), close(). The above code creates a template reference variable #dialog to get access to the dialog element, and then we call dialog.showModal() to open a modal window. That’s it. No dependency is needed, no CSS trick to create an overlay, no component library, and no jQuery.

Here is a link to a fully working example and a slighter longer tutorial with styling information for HTML dialogs.

ngrxLet: A better version of the async pipe

Yesterday, we mentioned how the async pipe can be used with *ngIf or *ngFor in our templates to declare a local variable that allows us to have multiple expressions using the same data:

<div *ngIf="user$ | async as user">
   <p>First name: {{ user.firstName }}</p>
   <p>Last name: {{ user.lastName }}</p>
</div>Code language: HTML, XML (xml)

While the above code works perfectly well, it’s not always convenient to add an additional *ngIf or *ngFor in our templates. Another shortcoming of the async pipe is that it doesn’t let us know if the observable has an error or completes successfully.

This is where the ngrxLet directive can save the day, as it solves all of the above shortcomings with a straightforward syntax. Our previous example becomes:

<div *ngrxLet="user$ as user">
   <p>First name: {{ user.firstName }}</p>
   <p>Last name: {{ user.lastName }}</p>
</div>Code language: HTML, XML (xml)

And if we want to get any errors or the completion status of the observable, we can do so with more local variables exposed by ngrxLet:

<div *ngrxLet="user$ as user; error as e; complete as c">Code language: HTML, XML (xml)

You can find a complete working example here. ngrxLet can be installed as a dependency using npm (npm install @ngrx/component), and it’s important to note that it is not the entire ngrx state management library, just a tiny subset of it, so using that directive does not require using anything else from ngrx.

Here is a link to a slightly expanded version of that tutorial with more information if you want to dig deeper into it: ngrxLet – A better version of the async pipe.

Async pipe syntax tricks

Yesterday, we wrote about how to use the async pipe to automatically subscribe and unsubscribe from our observables.

When I teach that topic, people usually have at least one of these two objections:

What if I also need the data from that subscription in my Typescript code?

At first, using the async pipe seems only to give you access to the data in your HTML templates. That isn’t the case, though, because you can still use the tap operator to “spy” on your observable and get the data from there. For instance (complete example here):

this.name$ = nameService.getName().pipe(
    tap(name => this.name = name)
);Code language: TypeScript (typescript)

And then in the HTML template:

<p>Name from async pipe: {{ name$ | async }}</p>Code language: HTML, XML (xml)

What if I need to read multiple properties from the object in that subscription?

Another way to put it is that you don’t want to end up doing something like this:

<p>First name: {{ (user$ | async)?.firstName }}</p>
<p>Last name: {{ (user$ | async)?.lastName }}</p>Code language: HTML, XML (xml)

The above code is pretty hard to read and requires one subscription for each property. This alone can be a disaster, as each subscription might trigger an HTTP request for the same data from the server!

Instead, you can do something like this, which uses only one subscription, stores the result in a local variable, then renders the data when it’s available. This technique works with any structural directive, such as *ngIf or *ngFor:

<div *ngIf="user$ | async as user">
   <p>First name: {{ user.firstName }}</p>
   <p>Last name: {{ user.lastName }}</p>
</div>Code language: HTML, XML (xml)

If changing the DOM structure by adding an element to accommodate that subscription bothers you, then you can use ng-template instead, though the syntax here can be a little bit unsettling, too:

<ng-template [ngIf]="user$ | async" let-user>
   <p>First name: {{ user.firstName }}</p>
   <p>Last name: {{ user.lastName }}</p>
</ng-template>Code language: HTML, XML (xml)

Ok, that’s probably plenty enough for today. Tomorrow, we’ll see how we can do even better than this.

Exported Directives

Yesterday, we talked about Template Reference Variables. Today, I want to show you how a directive can be accessed with a Template Reference Variable.

You’ve probably seen that syntax before:

<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)" >Code language: HTML, XML (xml)

That syntax is possible because the NgForm directive is exported using the following syntax (actual Angular source code here):

@Directive({
   exportAs: 'ngForm'
})Code language: TypeScript (typescript)

The above code enables the usage of Template Reference Variables such as #myForm="ngForm" . This technique is widely used in Angular forms and component libraries to expose public directive properties (and methods) to your component’s template.

For instance, we can access myForm.value or myForm.valid in an expression.

ngModel is exported that way, too.

Template Reference Variables

Welcome to the very first edition of my daily Angular Newsletter! As promised, I’ll keep it nice and short.


Today’s topic is Template Reference variables. I usually call that feature “the hashtag syntax” because that’s how it’s used:

<input #phone placeholder="phone number" />Code language: HTML, XML (xml)

Here’s how I could use that reference variable to get the value of the input, for instance:

<button (click)="callPhone(phone.value)">Call</button>Code language: HTML, XML (xml)

phone refers to the input element with the #phone attribute created earlier. That’s the template reference variable.
These variables can be used instead of ngModel, for instance. Even better, they also work with components and directives!

For instance, the following template reference variable hello would have access to all public properties and methods of the HelloComponent:

<app-hello #hello ></app-hello>Code language: HTML, XML (xml)

For more information and examples, you can read another short blog post of mine here.