RxJS provides many operators and methods to modify and control the data flow from the source obsevable. More detailed list here.
Eg.
Combine two requests
forkJoin([this.http.get('url'), this.http.post('url', '')]).subscribe
resposne => {
// resonse[0] - GET request response
// resonse[1] - POST request response
},
error => {
}
);
Pipe a response from one call as input to another request
this.http.get('url').pipe(
switchMap(getResponse => {
return this.http.post('url', getResponse)
}
).subscribe(
response => { },
error => { }
);
These are some of the most mundane advantages. RxJS would be more useful when multiple co-dependent observables need to handled.
In your case you could pipe in the map
operator to return the versionNumberField
property from the response.
return this.http.post<ILicenseInfo>(endpoint, params).pipe(
map(response: ILicenseInfo => response.versionNumberField)
);
You could then subscribe to it the component to retrieve the emitted values
public ngOnInit(): void {
this.aboutService.getVersionNumber().subscribe(
(response: string) => {
this.version = response;
}
);
}
Potential memory leaks
With the observables, there comes a risk of potential memory leaks from non-closed subscriptions. The Angular HTTP client has a built-in unsubscription mechanism, but that too can fail. So it is always better to close the subscription in the component (usually in the ngOnDestroy()
hook).
import { Subscription } from 'rxjs';
httpSubscription: Subscription;
public ngOnInit(): void {
this.httpSubscription = this.aboutService.getVersionNumber().subscribe(
(response: string) => {
this.version = response;
}
);
}
ngOnDestroy() {
if (this.httpSubscription) {
this.httpSubscription.unsubscribe();
}
}
There is also an elegant way of handling unsubscription using RxJS takeUntil
operator.
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
closed = new Subject<void>();
public ngOnInit(): void {
this.httpSubscription = this.aboutService.getVersionNumber().pipe(
takeUntil(this.closed)
).subscribe(
(response: string) => {
this.version = response;
}
);
}
ngOnDestroy() {
this.closed.next();
this.closed.complete();
}
So the susbcription will be active until the closed
observable is open.
But both these methods can get tedious very quick if you're handling multiple observables in multiple components. There is a clever workaround for this sourced from here.
First create the following export function
// Based on https://www.npmjs.com/package/ng2-rx-componentdestroyed
// Source credit: https://stackoverflow.com/a/45709120/6513921
import { OnDestroy } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs/ReplaySubject';
export function componentDestroyed(component: OnDestroy): Observable<void> {
const oldNgOnDestroy = component.ngOnDestroy;
const destroyed$ = new ReplaySubject<void>(1);
component.ngOnDestroy = () => {
oldNgOnDestroy.apply(component);
destroyed$.next(undefined);
destroyed$.complete();
};
return destroyed$.asObservable();
}
Now all there is to do is import the function, implement ngOnDestroy
hook in the component and pipe in takeUntil(componentDestroyed(this)
to the source observable.
import { takeUntil } from 'rxjs/operators';
public ngOnInit(): void {
this.httpSubscription = this.aboutService.getVersionNumber().pipe(
takeUntil(componentDestroyed(this)) // <-- pipe in the function here
).subscribe(
(response: string) => {
this.version = response;
}
);
}
ngOnDestroy() { } // <-- should be implemented
Update: async
pipe
The async
pipe can also be used to retrieve values asynchronously. It works with both observables and promises. When used with observables, it makes sure the subscription is always closed when the component is destroyed without any modifications in the controller.
Controller
version$: Observable<any>;
public ngOnInit(): void {
this.version$ = this.aboutService.getVersionNumber();
}
Template
<ng-container *ngIf="(version$ | async) as version">
Version is {{ version }}
<ng-container *ngIf="version > 5">
It is already the latest version available.
<ng-container>
</ng-container>
Variable version$
is named with the common convention of suffixing a dollar sign to Observable
types.