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,
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
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
courseObswith 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
So here is how we can to display this data on the screen: we would want to use the Angular
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
- the else clause is defining what to display while the data is not available (the
- the 'as' syntax is specifying a template variable for the expression
courseObs | async, and that variable is named
- 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
coursevariable available inside the
ngIfsection, that corresponds to the value emitted by the backend call
coursevariable is ready to be used, just like if the
courseobject 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.
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. If you are looking to use RxJs in the specific context of an Angular application, we recommend the Reactive Angular Course, where we cover lots of commonly used reactive design patterns for building Angular applications.
I invite you to subscribe to our newsletter to get notified when more posts like this come out:
If you are just getting started learning Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
If you enjoyed this post, have also a look also at other popular posts that you might find interesting:
- Angular Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular Components - The Fundamentals
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven vs Model Driven
- Angular ngFor - Learn all Features including trackBy, why is it not only for Arrays ?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work ?