Angular Reactive Templates with ngIf and the Async Pipe

The ngIf template syntax proves invaluable across numerous everyday scenarios, such as employing an else clause to present a loading indicator.

However, there's more depth to ngIf than initially meets the eye: the "ngIf as" syntax, when paired with the async pipe, is a cornerstone for crafting templates in a more reactive manner.

Adopting this template style offers several advantages:

  • fewer memory leak risks (contingent on the Observable types employed)
  • enhanced code readability
  • a reduction in potential complications arising from multiple subscriptions at the service layer
  • reduced state management within our components

This approach is an excellent strategy for creating more readable templates while proactively circumventing a range of common pitfalls.

Table Of Contents

This post will delve into the following subjects:

  • an initial examination of the ngIf Else syntax
  • a subsequent look at the "ngIf as" syntax
  • an attempt to construct a template in a non-reactive fashion, highlighting potential issues
  • a refactoring of that template into a reactive style using ngIf else and ngIf as, discussing the resulting benefits

Let's dive straight into our discussion on crafting Reactive Angular templates.

NgIf Else Example

First, let's examine the ngIf Else syntax in isolation with an example:

As observed, we can attach an else clause to ngIf, referencing a template by name. This template will replace the element bearing the ngIf directive when the condition evaluates to false.

Depending on the condition's truthiness, this will print either "Condition is true" or the contents of the loading template onto the annotated element.

The ngIf as Syntax

The ngIf directive also permits evaluating an expression's truthiness and assigning the expression's result—which may not be a boolean—to a variable.

Consider this example:

This template would display "Angular For Beginners" because the expression course is truthy. Notice that course refers to a component property holding a plain JavaScript object.

The expression's result isn't a boolean; it's an object. This object gets assigned to a local template variable named result, allowing the description property to be printed as expected.

A less obvious benefit of this syntax is how it significantly simplifies writing our templates in a more Reactive Style.

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

To grasp how the `ngIf as` syntax bolsters Angular's built-in reactive programming support, we need a more tangible example involving backend data loading.

We'll attempt to display this data using the async pipe. Imagine a CourseService fetching data asynchronously from a backend via the Angular HTTP module:

Let's also examine the structure of the custom Course type:

It consists of two mandatory properties, id and shortDescription, and three optional properties marked with a question mark.

Suppose our application loads this data and wants to display all course properties. Our component would look like this:

Let's dissect what's happening here:

  • we declare an Observable member variable named courseObs, initially undefined
  • we initialize courseObs with an Observable returned from the service layer
  • we are unaware of when the Observable will emit or what it will emit, only that it should be a Course instance

To display this data, we'd naturally turn to the Angular async pipe.

Why use the async pipe?

It automatically manages subscriptions and unsubscriptions as the component is instantiated or destroyed—a highly beneficial feature.

This is particularly critical for long-lived observables, such as those from the router or AngularFire.

Furthermore, it enhances code readability and declarativeness, reducing state variables within our component classes.

For instance, the component above only holds an observable member variable; we don't have direct data access at the component level—only the template accesses it.

So let's use the async pipe to render the course details:

This approach proves awkward because the async pipe must be used multiple times. This could lead to multiple subscriptions (and thus multiple HTTP requests), potentially causing further issues and harming readability.

In practice, we often resorted to this alternative to sidestep these problems:

Here, we've manually subscribed to the service's observable and assigned the backend result to a local variable. Our template simplifies significantly: just a variable named course to access the data.

However, this approach has potential drawbacks, especially in larger applications.

What are some potential problems with this approach?

While functional, a key concern is the requirement for manual subscription management.

This is inconsequential for HTTP observables, which emit once and complete. But for long-lived observables, it could create issues.

Another potential issue with this approach

Introducing a local variable to pass data to the template makes the program more imperative than the reactive approach of defining an Observable, passing it to the template, and declaring its usage there.

With state now stored in a component-level variable, there's a temptation to write code that mutates this state.

In this small example, it's harmless. But in a larger app, it might lead to maintainability issues.

Our goal was to use the async pipe for a more reactive style, avoiding these issues by design.

Instead, we fell back on manual subscriptions due to the impracticality of using the async pipe directly.

Another alternative - refactor into a smaller component

Is there a better way to avoid using the async pipe multiple times?

One option is to extract the course detail implementation into a separate component. This preserves the async pipe usage and avoids manual component-level subscriptions:

The course-detail component would be:

This is a viable option, especially if we plan to reuse the component elsewhere.

However, we've created an extra component mainly to circumvent multiple async pipe instances in the parent template.

It turns out there's a superior solution leveraging ngIf's features! Let's explore it.

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

By combining all of ngIf's features with the async pipe, we arrive at this solution:

Let's break down this example:

  • the async pipe subscribes to courseObs only once
  • the else clause defines a loading state (ng-template named loading) for when data isn't ready
  • the 'as' syntax creates a template variable, course, for the expression courseObs | async
  • this aliased course variable holds the course object emitted by the observable
  • a local course variable is now available within the ngIf section, corresponding to the backend's emitted value
  • this course variable is immediately usable, just as if the object was passed synchronously via an @Input()

Advantages of this more reactive approach

Writing templates in this reactive style offers these benefits:

  • no manual component-level subscriptions for service layer observables
  • no need for extra smaller components just to use the async pipe once and avoid multiple subscriptions
  • no local data state variables at the component level, minimizing risks from local state mutation
  • a more declarative codebase: components and templates are declarative, connecting data streams rather than storing and passing local variables

We achieve a highly readable template with less code and fewer potential failure points, such as memory leaks on long-running streams—the async pipe handles unsubscriptions transparently!

Additionally, we avoided both repeating the async pipe on the template and creating an intermediate component.

Conclusions

As demonstrated, the ngIf / else features are versatile, but particularly powerful when combined with the async pipe, simplifying Angular development in a more reactive style.

We hope this was insightful. For more on RxJS in Angular, consider the Reactive Angular Course, which covers numerous common reactive patterns for Angular apps.

Subscribe to our newsletter for notifications on future articles:

New to Angular? Check out the Angular for Beginners Course:

Angular Reactive Templates with ngIf and the Async Pipe — figure 1

If you found this post helpful, you might also enjoy these other popular articles: