Explore the future of Web Scraping. Request a free invite to ScrapeCon 2024

Angular: Use Observable Subscriptions & Async-Pipe to Prevent Memory Leaks.

Ionic4 hide header on scroll - how to hide header on content scroll in Ionic Framework.

image

Async-Pipe is an Angular built in tool to manage Observable-Subscriptions. We can easily simplify functionality of an Angular code using the async pipe. Let’s learn to use Async-Pipe | Observable-Subscriptions.

Async-Pipe manages Observable-Subscriptions, Observables are the type of variables whose values are being tracked whenever changed — to make sure we always get updated value. We subscribe to an Observable to get its updated value.

We’ll learn to use Async-Pipe:

Angular Async-Pipe

The angular async pipe ‘allows the subscription to observe value of anything inside of the angular template syntax’. It also takes care of unsubscribing from observables automatically.

#1 TO CREATE -> IMPLEMENT ‘Observable’.

In this example we’ll create a component with a very simple observable that increments a value by one in every second and outputs that value.
(Basically this observable is just counting up)
Example-1: Create a new Component or Open your own component (if you already have one). To create new, run this command👇👇

ng g component YourComponentName

Now, open up your ***.component.ts and update the below code👇👇
(Make sure to replace HomePageComponent with your component name. Similarly the name of selector, templateUrl and styleUrls will be replaced by your name.)

import { Component, OnInit } from "@angular/core";

import { Observable } from "rxjs";

@Component({
  selector: "app-home",

  templateUrl: "./home.page.component.html",

  styleUrls: ["./home.page.component.css"],
})

// we must 'implements OnInit' to use 'OnInit'
export class HomePageComponent implements OnInit {
  // declare an 'Observable', variable name: 'observableNumber'

  observableNumber: Observable<number>;

  ngOnInit() {
    // create an Observable

    this.observableNumber = Observable.create((observer) => {
      // initialize value of temp variable 'val' with 0

      let val = 0;

      const interval = setInterval(() => {
        // observer.next will announce the change made in 'val

        // by 'observableNumber'

        observer.next(val);

        // increment value of 'val' after every 1 sec

        val++;
      }, 1000);

      return () => clearInterval(interval);
    });
  }
}

(We must ‘implements OnInit’ to use ‘OnInit’. Learn more -> Click here👆)

#2 TO DISPLAY -> RESOLVE ‘Observable’.

To display the value we’ll reference the observable property and use the async pipe to resolve the observable:

Now, open up your ***.component.html and add this code👇👇

<p>{{ observableNumber | async }}</p>

Learn more about Interpolation syntax {{ }} –> Click here👆)

The output of ‘observableNumber’ will display the count value on your screen, and increment in every second.

#3 RESOLVE ‘Observable’ — without using ‘Async-Pipe’.

A very common use case of ‘Observable’ is to display values received from a REST-Endpoint/APIs because the Angular HttpClient returns an observable.
Here we will learn to ‘Resolve Observable’ returns from ‘REST-Endpoint/APIs’.

Why we need to use the async pipe?
There are many ways we can subscribe to observables. The default way (without angular) is to subscribe the observable manually and update a separate property with the value, check example below: 👇👇
Example-2: Open up your ***.component.ts and update the below code👇👇

import { Component, OnInit } from "@angular/core";

import { Observable } from "rxjs";

@Component({
  selector: "app-home",

  templateUrl: "./home.page.component.html",

  styleUrls: ["./home.page.component.css"],
})

// we must 'implements OnInit' to use 'OnInit'
export class HomePageComponent implements OnInit {
  // create an 'Observable', variable name: 'observableNumber'

  observableNumber: Observable<number>;

  // create another variable 'latestValue'

  // this variable will store the most updated value

  latestValue: number;

  ngOnInit() {
    // create an Observable

    this.observableNumber = Observable.create((observer) => {
      // THIS IS THE SAME CODE WE USED IN PREVIOUS EXAMPLE-1 TO 'create an Observable'

      // initialize value of temp variable 'val' with 0

      let val = 0;

      const interval = setInterval(() => {
        // observer.next will announce the change made in 'val

        // by 'observableNumber'

        observer.next(val);

        // increment value of 'val' after every 1 sec

        val++;
      }, 1000);

      return () => clearInterval(interval);
    });

    // THIS IS THE NEW CODE

    // subscribe to an Observable

    // make sure to save a reference to subscription to

    // be able to unsubscribe later

    this.observableNumber.subscribe((value) => {
      // this subscription make sure to have latest value always

      this.latestValue = value;
    });

    // ...
  }
}

We can now bind to the property without using the async pipe:
(instead of using ‘observableNumber’ we are using ‘latestValue’ to get updated value because we have already ‘subscribed observableNumber’ in the ‘ngOnInit’ function to resolve ‘Observable’)👇👇

Now, open up your ***.component.html and add this code👇👇

<p>{{ latestValue }}</p>

The output of ‘latestValue’ will display the count value on your screen — increment in every second — same as we see in previous example-1.

So why would we use the async-pipe then?

In Example-2 we subscribed to the observable manually, we also need to manually unsubscribe. Otherwise, we risk a memory leak when the component is destroyed.

To fix this, ‘we need to unsubscribe when the component is destroyed’. The best place to do that is the ‘ ngOnDestroy’ lifecycle hook:

Open up your ***.component.ts and update the below code👇👇

// import 'OnInit' and 'OnDestroy'

import { Component, OnInit, OnDestroy } from "@angular/core";

// also import 'Subscription'

import { Observable, Subscription } from "rxjs";

@Component({
  selector: "app-home",

  templateUrl: "./home.page.component.html",

  styleUrls: ["./home.page.component.css"],
})

// we must implements 'OnInit' and 'OnDestroy'
export class HomePageComponent implements OnInit, OnDestroy {
  // ...

  // create a variable to store subscription

  subscriptionToObservableNumber: Subscription;

  ngOnInit() {
    // create an Observable

    this.observableNumber = Observable.create((observer) => {
      // ...
    });

    // subscribe to an Observable

    // make sure to save a reference to subscription to

    // be able to unsubscribe later

    this.subscriptionToObservableNumber = this.observableNumber.subscribe(
      (value) => {
        // this subscription make sure to have latest value always

        this.latestValue = value;
      }
    );
  }

  // ngOnDestroy executes when component is destroyed

  ngOnDestroy() {
    // unsubscribe when the component is destroyed

    this.subscriptionToObservableNumber.unsubscribe();
  }
}

A cleaner and more reactive way of doing the same thing is to use the ‘rxjs takeUntil’ operator with another observable/subject that we complete when the component is destroyed. In this case, the ‘takeUntil’ operator is taking care of unsubscribing.

Open up your ***.component.ts and update the below code👇👇

// import 'OnInit' and 'OnDestroy'

import { Component, OnInit, OnDestroy } from "@angular/core";

// also import 'Subscription'

import { Observable, Subject, Subscription } from "rxjs";

import { takeUntil } from "rxjs/operators";

@Component({
  selector: "app-home",

  templateUrl: "./home.page.component.html",

  styleUrls: ["./home.page.component.css"],
})

// we must implements 'OnInit' and 'OnDestroy'
export class HomePageComponent implements OnInit, OnDestroy {
  // create an 'Observable', variable name: 'observableNumber'

  observableNumber$: Observable<number>;

  // create another variable 'latestValue'

  // this variable will store the most updated value

  latestValue: number;

  // create a variable to store subscription

  subscriptionToObservableNumber: Subscription;

  // declare unsubscribe$

  unsubscribe$: Subject<void> = new Subject<void>();

  ngOnInit() {
    // create an Observable

    this.observableNumber$ = Observable.create((observer) => {
      // ...
    });

    // the 'takeUntil' operator is taking care of unsubscribing

    this.observableNumber$

      .pipe(takeUntil(this.unsubscribe$))

      .subscribe((value) => {
        this.latestValue = value;
      });

    // subscribe to an Observable

    // make sure to save a reference to subscription to

    // be able to unsubscribe later

    this.subscriptionToObservableNumber = this.observableNumber$.subscribe(
      (value) => {
        // this subscription make sure to have latest value always

        this.latestValue = value;
      }
    );

    // create an Observable

    this.observableNumber$ = Observable.create((observer) => {
      // ...
    });
  }

  // ngOnDestroy executes when component is destroyed

  ngOnDestroy() {
    // unsubscribe when the component is destroyed

    this.unsubscribe$.next();

    this.unsubscribe$.complete();
  }
}
  • This approach is especially useful when dealing with multiple observables per subscription, as we do not need to keep a list with all subscriptions.
  • After all, this additional syntax is not necessary when using the angular async pipe, as the pipe itself takes care of unsubscribing from the observable once the component is destroyed. So, if nothing else, the async pipe makes our code cleaner.
  • Also, the methods showed above do not work with the onPush change detection strategy, which is used to do performance optimizations of components. Async pipe, on the other hand works just fine with that.

That is why “we should definitely use the async-pipe wherever and whenever possible”.

#4 ASYNC-PIPE with *ngIf and *ngFor

With *ngIf
Interpolation {{ }} is not the only data binding to which the async pipe can be used. We can also use it with the *ngIf directive:

Now, open up your ***.component.html and try this code👇👇

<p *ngIf="(observableNumber$ | async) > 5">{{ observableNumber$ | async }}</p>

Note, that the braces inside the *ngIf directive are absolutely necessary in this case.

The above

will only be visible when the value of ‘observableNumber$’ becomes greater than 5.

With *ngFor
We can use the async pipe for *ngFor the same way we use for *ngIf directive.
But to use the async-pipe we must need the observable of type array, not just a single value. See this👇👇

// ...

@Component({
  // ...
})

// we must implements 'OnInit' and 'OnDestroy'
export class HomePageComponent implements OnInit, OnDestroy {
  // ...

  items$: Observable<number[]>;

  constructor() {}
}

Now, we can use this in *ngFor directive like this👇👇

<p *ngFor="let item of items$ | async">{{ item }}</p>



Continue Learning