DEV Community

loading...

Angular Component Subscription vs. AsyncPipe: Use Pipes When Possible

Eyas
Software Engineer @ Google. New York, NY.
Originally published at blog.eyas.sh Updated on ・1 min read

I typically review a fair amount of Angular code at work. One thing I typically encourage is using plain Observables in an Angular Component, and using AsyncPipe (foo | async) from the template html to handle subscription, rather than directly subscribing to an observable in a component TS file.

Subscribing in components

Unless you know a subscription you're starting in a component is very finite (e.g. an HTTP request with no retry logic, etc), subscriptions you make in a Component must:

  1. Be closed, stopped, or cancelled when exiting a component (e.g. when navigating away from a page),
  2. Only be opened (subscribed) when a component is actually loaded/visible (i.e. in ngOnInit rather than in a constructor).

AsyncPipe can take care of that for you

Instead of manually implementing component lifecycle hooks, remembering to subscribe and unsubscribe to an Observable, AsyncPipe can do that for you.

See code examples and the full post here.

Discussion (3)

Collapse
mnx_vsh profile image
Manish

Thanks for explain this, I have my doubts: As I am starting to learn angular I got confused .

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
selector: 'my-app',
template: `

{{a.title}}


`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
public name = 'Angular';
public dataValues:object;

public configUrl = 'jsonplaceholder.typicode.com/posts';
constructor(private http: HttpClient) { }

showConfig() {
return this.http.get(this.configUrl)
.subscribe(data => this.dataValues = data);
}
}

Is that line return this.http.get(this.configUrl) return observable , If yes then why din't we import observable from rxjs.

Why do we import Observable with simple code like :

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface Config {
title: string;
body: string;
}

@Component({
selector: 'my-app',
template: `

{{a.title}}


`,
styleUrls: ['./app.component.css']
})

export class AppComponent {
public name = 'Angular';
public dataValues: object;
public config: Config;

public configUrl = 'jsonplaceholder.typicode.com/posts';
constructor(private http: HttpClient) { }

showConfig() {
return this.http.get(this.configUrl)
.subscribe((data: Config) => this.dataValues = data);
}

}

Collapse
eyassh profile image
Eyas Author

Is that line return this.http.get(this.configUrl) return observable , If yes then why din't we import observable from rxjs.

Yes, the this.http.get(...) line returns an Observable. In general my code snippets don't include the imports.

In general you can import specific types you depend on but that isn't actually strictly necessary. The implementation of HttpClient.get imports Observable and returns it, so all you need to do is include the implementation of HttpClient, which you do.

In general ES6 imports are for things you directly depend on. Those imports will transitively import any things they depend on.

Collapse
mralanguevara profile image
Alan Guevara

Thanks for the article.

I am confused about something.

When I use async pipe

ngOnInit() {
myData$ = httpCall(....);
}

...

[data]="myData$ | async">

And lets say that later in the same component I update my data by doing another httpCall and assigning it to my observable:

updateData() {
myData$ = httpCall(....);
}

This actually works and my child component gets updated. But, if ?I subscribe to it instead:

ngOnInit() {
myData$ = httpCall(....).subscribe(data => {
this.myData = data;
});
}

...

[data]="myData">

And later I make another call:

updateData() {
myData$ = httpCall(....);
}

MyData inside the child component only updates on the first call but not on the second one...
Does a subscription completes the observable after the first time?

The weird thing is if I subscribe but using NGRX:

ngOnInit() {
myData$ = this.store.dispatch(new MyDataAction());
myData$.subscribe(data => {
this.myData = data;
});
});
}

...

[data]="myData">

And then I try to update:

updateData() {
myData$ = this.store.dispatch(new MyDataAction());
}

It updates my child component the second time!!!

Why is this happening?