Angular Reactive Templates with ngIf and the Async Pipe

The ngIf template syntax is very useful in many common use cases, like for example using an else clause when we want to display a loading indicator.

But it turns out that there is more to ngIf than it might seem at first sight: The "ngIf as" syntax combined with the async pipe is also a key part for being able to write our templates in a more reactive style.

There are a lot of benefits of writing our templates this way:

  • memory leaks are less likely (depending on the type of Observables we use)
  • more readable code
  • less potential issues with multiple subscriptions at the level of the service layer
  • less state in our components

Overal this is a great way to write more readable templates while avoiding by design a number of common problems.

Table Of Contents

In this post, we will cover the following topics:

  • we will start by covering the ngIf Else syntax
  • we will then cover the "ngIf as" syntax
  • we will try to write a template in a none reactive style and discuss potential problems we might run into
  • we will then refactor the template to a more reactive style, using the ngIf else and ngIf as syntaxes and discuss the benefits of this approach

So without further ado, let's get started with our design discussion on Reactive Angular templates!

NgIf Else Example

Let's first have a look first at the ngIf Else syntax, in isolation, here is an example:

As we can see, we can specify in ngIf an else clause with the name of a template, that will replace the element where ngIf is applied in case the condition is false.

This would either print "Condition is true" or the content of the loading template to the element annotated with ngIf, depending on the truthiness of the condition.

The ngIf as Syntax

With ngIf, its also possible to evaluate the truthiness of an expression, and assign the result of the expression (which might not be a boolean) to a variable.

Let's have a look at an example:

The template above would print "Angular For Beginners" because the expression course is truthy. As we can see, course corresponds to a component property which is a plain Javascript object.

But the result of the expression is not a boolean, its an object and it gets assigned to a local template variable named result, and so the description property gets printed to the screen as expected.

What might not be apparent when first encountering this syntax, is that it makes it much simpler to write our templates in a more Reactive Style.

What is the relation between the ngIf as syntax and Reactive Programming?

In order to understand how the ngIf as syntax is an important part of the Angular built-in reactive programming support, we will need a more concrete example where we will load some data from the backend.

We will then try to display the data on the screen using the async pipe. Imagine that we have a CourseService, that brings some data asynchronously from the backend, using, for example, the Angular HTTP module:

Let's also have a look at what the Course custom type looks like:

As we can see, it's made out of two mandatory properties id and shortDescription, and three more optional properties that are annotated with a question mark.

Let's say that now our application loads this data from the backend and we want to display it on the screen (all the course properties). Here is what our component would look like:

So what is happening here? Let's break it down:

  • we are defining an Observable member variable named courseObs, that is initially undefined
  • we are initializing courseObs with an Observable that is returned by the service layer
  • we don't know when the Observable will return or what it will return, other than it should be a Course instance

So here is how we can to display this data on the screen: we would want to use the Angular async pipe.

Why use the async pipe ?

Because it automatically subscribes and unsubscribes from Observables as the component gets instantiated or destroyed, which is a great feature.

This is especially important in the case of long-lived observables like for example certain Observables returned by the router or by AngularFire.

Also because it makes our programs easier to read and more declarative, with fewer state variables in our component classes.

For example, notice that the component above only has an observable member variable and that we don't have direct access to the data at the level of the component itself - only the template accesses the data directly.

So let's then use the async pipe to print the course details to the screen:

As we can see, this ends up not being very convenient, because we need to use the async pipe multiple times. Actually, this would cause multiple subscriptions (meaning multiple HTTP requests), which could lead to other problems and also it's not as readable.

So in practice this is what we often ended up doing instead to avoid these issues:

As we can see, we have subscribed to the observable returned by the service layer and defined a local variable that contains the result of the backend call. Now our template is a lot simpler: we simply have a variable named course that we can use to access the data.

But there could be a couple of potential problems with this, especially in a larger application.

What are some potential problems with this approach?

This approach works great, but one potential issue is that now we have to manually manage the subscriptions of this Observable.

In the case of HTTP observables this does not have an impact because those Observables emit only once and then they complete, but in the case of long-lived Observables this could potentially cause an issue.

Another potential issue with this approach

Also, we have defined here a local variable to pass data to the template, which ends up making our program more imperative if compared to the more reactive approach of simply defining an Observable, passing it to the template and declaring on the template how to use it.

Because now we have here the state stored on this variable at the level of the component, we might be tempted to further write code that mutates that state.

Here in this small example, it would not cause an issue, but in a larger application, this could potentially cause some maintainability problems.

By using the async pipe we were looking to write a program in a more reactive style, where those couple of potential problems are avoided by design.

But instead, we ended up using manual subscriptions as we could not find a practical way of using the async pipe.

Another alternative - refactor into a smaller component

Let's see if there is a better way: can we avoid using the async pipe multiple times?

We could, for example, move the course detail implementation to a new component. This would allow us to keep using the async pipe, and avoid the manual subscription at the level of the component:

And this is what the course-detail component would look like:

This is a good alternative, because we might end up using this component in other places of the application.

But we ended up needing to create a separate component mostly to avoid having to use the async pipe multiple times in the parent template.

It turns out that there is a much better solution, where we will leverage some of the features of ngIf! Let's have a look.

Reactive Style Templates - The ngIf as syntax and the async pipe

If we combine all the available features of ngIf with the async pipe, we can now come up with the following solution:

So what is going on in this example? Let's break it down:

  • the async pipe is being used to subscribe only once to courseObs
  • the else clause is defining what to display while the data is not available (the ng-template named loading)
  • the 'as' syntax is specifying a template variable for the expression courseObs | async, and that variable is named course
  • the result of this expression is aliased as course, and corresponds to the course object emitted by the course observable
  • now there is a local course variable available inside the ngIfsection, that corresponds to the value emitted by the backend call
  • This course variable is ready to be used, just like if the course object had been passed synchronously as an @Input() to this component

Advantages of this more reactive approach

Here are the advantages of writing our templates in this more reactive style:

  • There are no manual subscriptions at the component level for observables coming out of the service layer
  • we don't have to create smaller components to be able to use the async pipe only once and prevent multiple subscriptions
  • no local data state variables are defined at the level of the component, so its less likely to run into issues caused by mutating local component state
  • we now have a more declarative code: both the component and the template are very declarative, we are simply plugging in together streams of data instead of storing local variables and passing them to the template

We end up with a very readable template, less code and fewer things that can go wrong, like memory leaks on long-running streams - because the async pipe will take care of unsubscriptions transparently!

Also, there was no need to repeat the async pipe multiple times on the template, or creating an intermediate component just to avoid that.

Conclusions

As we could see in this example, the ngIf / else features although useful in many other cases, are especially useful when combined with the async pipe for simplifying the development of Angular applications in a more reactive style.

I hope you enjoyed the post, I invite you to have a look at the list below for other similar posts and resources on Angular.

I invite you to subscribe to our newsletter to get notified when more posts like this come out:

Video Lessons Available on YouTube

Have a look at the Angular University Youtube channel, we publish about 25% to a third of our video tutorials there, new videos are published all the time.

Subscribe to get new video tutorials:

Other posts on Angular

If you enjoyed this post, have also a look also at other popular posts that you might find interesting: