If we have a look at the Angular core roadmap, one of the main items is the continuous improvement of the support for building Angular Applications in Reactive style.
There are a couple new features originally introduced in the Angular 4 release that really help us write programs in a more reactive style, if we want to use that approach.
What on a first look could seem like a small improvement in template syntax (
ngIf related) turns out to be an important improvement towards writing applications in a more reactive style.
Let's first have a look first at the improvements on
ngIf in isolation. So what has changed ?
With Angular 4, we can now add an else clause to
ngIf, which was not available before. Let's have a look at what this looks like:
As we can see, we can now 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.
Another improvement on ngIf
So this is already really handy for many cases. So what other improvements does this syntax have ? We can also 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.
These are two great extra features for building our templates, but in what way do they help also to improve the support for reactive programming ?
It has to do with the
async pipe, and the way we can use it to build reactive programs.
The async Pipe early functionality in Angular
Let's give a simple example, 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, its 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 less 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, which could lead to other problems and also its 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 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 approach could potentially cause some maintainability problems.
By using the async pipe we where looking to write a program in a more reactive style where those couple of potential problems are avoided by design. So let's see if we can still do that.
Another alternative - refactor into a smaller component
Or alternatively, we could also 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.
In Angular 4 there is now a much better way and it ties back to the
ngIf new features: let's have a look.
Improved reactive programming support in Angular 4
If we combine all the available features of
ngIf in Angular 4 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 the data stream of
- now there is a
coursevariable available inside the
Advantages of this more reactive approach
As we can see in Angular 4 we can now simply combine the
async pipe with the new
ngIf features, and have all the features combined:
- 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
As we could see in this example, the new
ngIf 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 of other similar posts and resources on Angular below:
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:
- Angular 2 Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular 2 Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular 2 Components - The Fundamentals
- How to build Angular 2 apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular 2 Forms - Template Driven vs Model Driven
- Angular 2 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 2
- How does Angular 2 Change Detection Really Work ?