in Angular is it a bad practice to use .subscribe() in components?
I've read that using .subscribe in component is considered a bad practice and that async pipe is a better alternative.
I'm asking for examples and why.
No, it is not bad practice to use .subscribe() within components as long you unsubscribe from those observables when components are destroyed (i.e. within the ngOnDestroy() method). The async pipe automatically unsubscribes from its observable which is why it is the recommended approach.
If you do not unsubscribe from your observables when your components are destroyed, you will end up with "memory leaks", or rather objects that won't be garbage collected because there still exists a reference to them. You might also see strange behavior because there are phantom subscriptions still out there. Components will be destroyed at times such as when navigating to different routes if you're using the router or when child components within an *ngFor are refreshed.
For my projects, I make myself an abstract parent class for components that handles the unsubscribing for me.
import { OnDestroy } from'@angular/core';
import { Subscriber, Subscription } from'rxjs/Rx';
/**
* Class `BaseComponent` encapsulates common logic for components such as providing
* a mechanism to automatically unsubscribe from Rx subscriptions upon destruction.
*/
export abstractclassBaseComponentimplementsOnDestroy {
protected _subscriptions: { [key: string]: Subscriber<any>|Subscription> } = {};
protectedgetsubscriptions() {
returnthis._subscriptions;
}
constructor() {}
ngOnDestroy() {
for (let name inthis.subscriptions) { // tslint:disable-line:prefer-constif (this.subscriptions.hasOwnProperty(name)) {
const subscription = this.subscriptions[name];
if (subscription.unsubscribe) {
subscription.unsubscribe();
this.subscriptions[name] = null;
} else {
console.warn(`Property '${name}' on 'subscriptions' was not unsubscribed.`);
}
}
}
}
}
Then in your components you simply have to save any subscriptions into the subscriptions object with an arbitrary but unique name.
Matt Strom
Software Engineer, TypeScript ninja
No, it is not bad practice to use
.subscribe()within components as long you unsubscribe from those observables when components are destroyed (i.e. within thengOnDestroy()method). Theasyncpipe automatically unsubscribes from its observable which is why it is the recommended approach.If you do not unsubscribe from your observables when your components are destroyed, you will end up with "memory leaks", or rather objects that won't be garbage collected because there still exists a reference to them. You might also see strange behavior because there are phantom subscriptions still out there. Components will be destroyed at times such as when navigating to different routes if you're using the router or when child components within an
*ngForare refreshed.For my projects, I make myself an abstract parent class for components that handles the unsubscribing for me.
import { OnDestroy } from '@angular/core'; import { Subscriber, Subscription } from 'rxjs/Rx'; /** * Class `BaseComponent` encapsulates common logic for components such as providing * a mechanism to automatically unsubscribe from Rx subscriptions upon destruction. */ export abstract class BaseComponent implements OnDestroy { protected _subscriptions: { [key: string]: Subscriber<any>|Subscription> } = {}; protected get subscriptions() { return this._subscriptions; } constructor() {} ngOnDestroy() { for (let name in this.subscriptions) { // tslint:disable-line:prefer-const if (this.subscriptions.hasOwnProperty(name)) { const subscription = this.subscriptions[name]; if (subscription.unsubscribe) { subscription.unsubscribe(); this.subscriptions[name] = null; } else { console.warn(`Property '${name}' on 'subscriptions' was not unsubscribed.`); } } } } }Then in your components you simply have to save any subscriptions into the
subscriptionsobject with an arbitrary but unique name.@Component({ /* ... */ }) export class MyComponent extends BaseComponent { constructor() { super(); this.subscriptions['thisNameIsArbitrary'] = Observable.interval(1000) .subscribe(); } }