I'm having real trouble wrapping my head around observables and subscriptions in angular2. The problem I am currently having is the following:
I have a service which contains methods to post and get data from an API. The service is injected into a component, which directly calls those methods in the service. The service then retrieves data and stores it itself, but I then want to process that data within the component. I cannot work out how to have the component execute a function after the service has retrieved and stored the data itself.
service.ts
import { Injectable } from 'angular2/core';
import { Http } from 'angular2/router';
@Injectable()
export class Service {
result: Object;
constructor(http: Http) {
this.http = http;
}
httpGet(url) {
return this.http.get(url).subscribe(
result => this.result = result.json(),
error => console.log(error),
() => {}
)
}
}
component.ts
import { Component } from 'angular2/core';
import { Service } from './service';
@Component({
...
})
export class Component {
formattedResult: Object;
constructor(service: Service) {
this.service = service;
this.service.httpGet('/api')
// How do I call format result on service.result only after it is completed?
this.formatResult(service.result) // Need to execute this after the http call is completed
// I want something like:
this.service.httpGet('/api').then(() => formatResult(this.service.result));
}
formatResult(result) {
this.formattedResult = result.map(x => x.length) // For example
}
}
To answer my own question:
In the app root, import Rxjs with:
import 'rxjs/Rx';
This gives you access to the complete Observable object (not just the 'Observable-lite' included with Angular). This enables you to .map .reduce etc. the Http requests.
You can now use .map on an Http request to carry out arbitrary code in the context of the service, even if it is the component that subscribes to the result. So to achieve what I set out to do in the beginning, we can:
service.ts
import { Injectable } from 'angular2/core';
import { Http } from 'angular2/router';
@Injectable()
export class Service {
result: Object;
constructor(http: Http) {
this.http = http;
}
httpGet(url) {
return this.http.get(url).map(
result => {
let data = result.json();
this.result = data;
return data
}
)
}
}
component.ts
import { Component } from 'angular2/core';
import { Service } from './service';
@Component({
// Component setup
})
export class Component {
formattedResult: Object;
constructor(service: Service) {
this.service = service;
this.service.httpGet('/api').subscribe(
data => this.formatResult(data);
);
}
formatResult(result) {
this.formattedResult = result.map(x => x.length) // For example
}
}
Thanks to Gunter and Mark for the replies, helped me wrap my head around this a bit and I feel like I understand Observables much better having gone through a lot of the docs solving this problem!
Check if the result already arrived, if yes, create a new promise and complete it with the result, not not, fetch it, and return it as promise as well.
@Injectable()
export class Service {
result: Object;
constructor(http: Http) {
this.http = http;
}
httpGet(url) {
if(result === undefined) {
return this.http.get(url).toPromise().then(
result => this.result = result.json(),
error => console.log(error);
);
} else {
return new Promise().resolve(result);
}
}
}
I don't know TS and this code might contain some errors (I use Angular only Dart), but you should get the idea.
See also
- http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
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