I am working on an Angular(v4) application. Like any other web app, this app also has HTTP
requests and for each of these requests, I have a subscribe to get the data and show them on the UI. I have been reading about of use of async
pipe and why it is better than subscribe
. From what I have read, it surely has benefits over normal subscribe
method and I am convinced that I should use it in my application but I am not sure how to structure my app with this aync
pipe instead of subscribe
. Let me post the structure of my application:
I have a service layer that issues the Http
request to backend, something like:
some-service.component.ts
@Injectable()
export class SomeService {
constructor(private http: HttpClient) {
}
getSomething() {
return this.http.get(`${this.baseUrl}/${'test'}`);
}
}
The above service provides an Observable to my component, where I usually subscribe to it as shown below:
some-component.ts
@Component({
selector: 'some-app',
templateUrl: './some.component.html',
styleUrls: []
})
export class SomeComponent {
constructor(private someService: SomeService) {
}
getSomething() {
this.someService
.getSomething()
.subscribe(
// the first argument is a function which runs on success
(data) => {
// DO SOME DATA TRANSFORMATION HERE
},
// the second argument is a function which runs on error
(err: HttpErrorResponse) => {
this.exceptionService.errorResponse(err);
},
// the third argument is a function which runs on completion
() => {
// MAKE SOME FUNCTION CALL
}
);
}
}
Above is a general structure as how I do a subscribe for a general HTTP
response. I want to get rid of this subscribe
and use async
instead. However, my issue is, I do some data transformation
upon receiving the data (as shown above) or sometime I make some function call from the
completionobserver
(as shown above) and with such operations, I am not sure as how I am going to these operations once switch to async
.
For data transformation, I guess, I can do it in my service (not sure though) but how can I make function call as I do it now.
Any input would be highly appreciated. Also let me know if my question needs more clarifications. Thanks!
I will answer based on your code in more generic way
foo.service.ts
@Injectable()
export class FooService {
constructor(private _http:HttpClient){ }
public getFoo():Observable<any> {
return this._http.get(`${API_URL}`).map(r => r.json());
}
}
In map operator you can do manipulations and return new state
foo.component.ts
Import rxjs finally
operator to invoke method at final
import 'rxjs/add/operators/finally';
export class FooComponent {
public bar$:Obervable<any>;
constructor(private _foo:FooService){}
getBar(): void {
this.bar$ = this._foo.getFoo()
.finally(() => {
// INVOKE DESIRED METHOD
});
}
}
foo.html
<div *ngIf="bar$ | async; let bar">
<h3> {{bar}} </h3>
</div>
This will create div wrapper element and will provide visiblity hidden option to it, so there's no need for ?. 'Elvis' operator
You can also modify ngIf expression based on your requirement
When you work with observables, you can use a very common operator to transform the incoming data into something else : the map operator (documentation here). "The Map operator applies a function of your choosing to each item emitted by the source Observable, and returns an Observable that emits the results of these function applications."
some-service.component.ts (modified)
// Don't forget this, or it will cause errors
import 'rxjs/add/operator/map';
@Injectable()
export class SomeService {
constructor(private http: HttpClient) {
}
getSomething() {
return this.http.get(`${this.baseUrl}/${'test'}`)
.map(data => transformData(data));
}
some-component.ts
@Component({
selector: 'some-app',
templateUrl: './some.component.html',
styleUrls: []
})
export class SomeComponent {
constructor(private someService: SomeService) { }
getSomething() {
this.someService
.getSomething()
.subscribe(
// the first argument is a function which runs on success
(data) => {
// DO SOMETHING WITH DATA TRANSFORMED HERE
},
// the second argument is a function which runs on error
(err: HttpErrorResponse) => {
this.exceptionService.errorResponse(err);
},
// the third argument is a function which runs on completion
() => {
// MAKE SOME FUNCTION CALL
}
);
}
}
Note that you could also use map operator a second time in some-component.ts (after importing it). You could also decide to use it one time, only in some-component.ts. Or your could pass a function to this.someService.getSomething(myFunction) to transform the data. See the service implementation below.
// Don't forget this, or it will cause errors
import 'rxjs/add/operator/map';
@Injectable()
export class SomeService {
constructor(private http: HttpClient) {
}
getSomething(myFunction) {
return this.http.get(`${this.baseUrl}/${'test'}`)
.map(data => myFunction(data));
}
To use async pipe, store your Observable in a property.
import { Component, OnInit } from '@angular/core'
@Component({
selector: 'some-app',
templateUrl: './some.component.html',
styleUrls: []
})
export class SomeComponent implements OnInit {
myObservable;
constructor(private someService: SomeService) { }
ngOnInit() {
this.myObservable = this.getSomething();
}
getSomething() {
return this.someService.getSomething();
}
}
Then, apply the async pipe to your template like this (documentation here) :
<p>{{ myObservable | async }}</p>
Or, as said Optiq (when you deal with objects for exemple) :
<div *ngFor="let a of myObservable | async">
<p>{{ a.whatever }}</p>
</div>
Note that you must not apply async pipe to a function.
Finally, you could handle the case when data has been received. See below :
import { Component, OnInit } from '@angular/core'
@Component({
selector: 'some-app',
templateUrl: './some.component.html',
styleUrls: []
})
export class SomeComponent implements OnInit {
myObservable;
constructor(private someService: SomeService) { }
ngOnInit() {
this.myObservable = this.getSomething();
}
getSomething() {
const obs = this.someService.getSomething();
obs.subscribe((data) => {
// Handle success
}, (err) => {
// Handle error
}, () => {
// Make some function call
})
return obs;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With