fbpx Skip to content

Aquent | DEV6

Angular 4: Enhancing your Templates With ‘else’ & ‘as’

Written by: James McGeachie

Now that the Angular team has established Semantic Versioning going forward, major version numbers will only indicate some breaking changes, not full rewrites of the framework. With that said, we can still expect each major version number change to provide some interesting changes and additions. This is true for Angular version 4.0.0, which introduced a few new things to play with. In this post we’ll be discussing the addition of the ‘else’ and ‘as’ keywords. 

Structural Directives

As a brief refresher, two of the most commonly used directives in Angular are the built-in structural directives *ngIf and *ngFor. These directives are used very regularly, so any changes to them have the power to make a big difference to development. Let’s remind ourselves of what they do and discuss the additions. 

*ngIf 

<div *ngIf=“isLoaded”>

           <movies-list [movies]=“movies”></movies-list>

</div>

<div *ngIf=“!isLoaded”>

           <spinner></spinner>

</div>

In the above example we demonstrate a use case of *ngIf. We’re waiting for a property called ‘isLoaded’ to be true and if it is (presumably once we receive some data asynchronously), we’re showing a list of movies. When it’s not true, we’re showing a spinner component to indicate to the user that we’re waiting.

You may have seen a template like this before. So this seems good enough right? Well, we can think of improvements. One thing we might notice is that, from looking at the first div alone, we can’t immediately tell what will happen if ‘isLoaded’ is NOT true. We know this element won’t be displayed, but what will be displayed? For that answer, we have to go and find the spinner component. In this case, it’s nearby, but what if it was on line 50? Wouldn’t it be nice to be able to tell on the same line what would happen? Well, thanks to the thoughtfulness of the Angular team, now we do: the else condition. 

Introducing ‘else’

Let’s re-write this template utilizing our new keyword:

<div *ngIf=“isLoaded; else spinner”>
           <movies-list [movies]=“movies”></movies-list>

</div>

<ng-template #spinner>

           <spinner></spinner>

</ng-template>

We’ve replaced our div container for the spinner with an ng-template and have assigned a local variable ‘spinner’ to it. Also, in our *ngIf directive we’ve added ‘else spinner’. Now we know from reading just the first line that if loading is not complete, we’re displaying a spinner. Neat!

Note that with this syntax we reference a separate template for the else condition, but for the if condition we display the contents within the tag the *ngIf is attached to. Some people may not like that there’s an inconsistency here. If you do feel this way, you may prefer the following syntax:

<div *ngIf=“isLoaded; then movieList else spinner”></div>

<ng-template #movieList>

           <movies-list [movies]=“movies”></movies-list>

</ng-template>

 

<ng-template #spinner>

           <spinner></spinner>

</ng-template>

Moving the movie-list component from the original div and using the new ‘then’ keyword, we can use the same variable referencing pattern for both conditions. The downside to this version is that we now have what looks like an empty div being used to host the *ngIf directive. You may decide you prefer the prior syntax for this reason.

*ngFor

For a quick reminder about how to use *ngFor, let’s look inside our movie-list component:

<ul>

           <li *ngFor=“let movie of movies”>

                      {{ movie.title }}

           </li>

</ul>

Very simple, we iterate over the list of movies and display the name for each. Now, what if we want to make this a list of top movies instead of all movies? We could use the slice pipe.

<ul>

           <li *ngFor=“let movie of movies | slice:0:5”>

                      {{ movie.title }}

           </li>

</ul>

So now we’re only displaying the first 5 movies in the array. However, right now we as the code reader don’t know what these 5 movies signify, or how each of them rank. If we had a reference to the slice itself, that would help us out here. Turns out we can do that in a clean way now.

Introducing ‘as’

Time to make some additions:

<ul>

           <li *ngFor=“let movie of movies | slice:0:5 as topMovies; index as i”>

                      {{ topMovies.length - i }}: { movie.title }}

           </li>

</ul>

Using the new ‘as’ keyword we are able to neatly store the sliced array in a topMovies variable, which both gives us an indicative name, and allows us to reference it later. We can also alias index as i with shorter syntax than ‘let i = index’ . Lastly, it turns out we wanted to count down in reverse and having these two variables allows us to do so easily.

However, there are other ways to extract a section of an array. You may do so with an an Observable operator instead, so that logic is handled asynchronously and is kept out of the view. When it comes to working with Observables, using the async pipe can be very handy.

Async and ‘as’

We’ve decided to make our movie list an Observable. Our code has been rewritten:

<ul>

           <li *ngFor=“let movie of movies$ | async”>

                      {{ movie.title }}

           </li>

</ul>

<p>{{ ( movies$ | async)?.length }} movies</p>

We’re now iterating over an array that’s returned from an Observable and, we’ve also decided to print out the number of movies in our list. There are some problems here. Firstly, we’re repeating the pipe, which will re-run when changes occur. This could be a performance concern if we follow a pattern like this across an app. Secondly, we have some nasty syntax where we’re doing a null check on the movies after the async pipe that’s introduced some parenthesis. This is getting harder to read, so let’s improve it. 

<div *ngIf=“movies$ | async as movies”>

           <ul> 

                      <li *ngFor=“let movie of movies”>

                                 {{ movie.title }}

                      </li>

           </ul>

           <p>{{  movies.length }} movies</p>

<div>

By using the as keyword in a parent component to store a reference to the piped value, we only need to use the async pipe once and can write the nested code as though it was synchronous. More readable and potentially more performant!

Our Thoughts

All template syntax is only as good as you make it. The introduction of these new keywords does not mean you need to suddenly scatter your markup with them. Today we looked at a few use cases that show how these could benefit your code, but it’s up your team to make that decision based on your preferences.

Thanks for reading. Keep checking our blogs for more discussion on new Angular features as they’re introduced. 

Sign up for our Angular Course

Learn the most recent version and start building your own Angular apps

View Course Details