Reputation: 1985
Let's say I have a service getting a list of properties:
export class PropertyService {
private properties: any;
constructor(@Inject(Http) http) {
this.http.get('/properties.json')
.map((res:Response) => res.json())
.subscribe(
data => this.properties = data
);
}
getProperty(property: string) {
this.properties[property];
}
}
The problem is that properties
is not yet loaded when getProperty
is called.
How can I make getProperty
to wait for the subscription to populate the array?
I would rather not return a subscription to the components.
EDIT:
I tried paulpdaniels answer and worked with pluck
. But I got stuck pretty fast.
So basically I have this PropertyService which returns a HOST
.
I have an ApiService that uses that host to do another ajax call to get data.
export class PropertyService {
private properties: any;
constructor(@Inject(Http) http) {
this.properties = http.get('/properties.json')
.map((res:Response) => res.json()).cache(1);
}
getProperty(property: string) {
this.properties.pluck(property);
}
}
export class ApiService {
private http: Http;
private host: string;
constructor(@Inject(Http) http, @Inject(PropertyService) propertyService) {
this.http = http;
this.host = propertyServiceService.getProperty("API.URL"); //Is a subscription
}
getData(): any {
//Here I must subscribe to host, and once available,
//use host to do an ajax call that returns another Observable for the component
}
}
How to realize this?
Upvotes: 0
Views: 124
Reputation: 18663
Short answer is you can't, at least not without introducing some level of asynchronicity to your service.
The simple fact is there is no way to force blocking behavior and wait for the Observable
to complete, you should return an Observable
which your components should then also know how to consume.
export class PropertyService {
private properties: Observable<any>;
constructor(@Inject(Http) http) {
this.properties = this.http.get('/properties.json')
.map((res:Response) => res.json())
//This will hold onto a cached value of the result
//without re-executing
.cache(1);
}
getProperty(property: string) {
this.properties.pluck(property);
}
}
Edit 1
As Frank Herbert once wrote (paraphrasing) "the stream must flow!". If you need to use nested Observables
operators like flatMap
allow you to flatten those into a single stream, which you can continue to chain.
export class ApiService {
private http: Http;
private host: Observable<string>;
constructor(@Inject(Http) http, @Inject(PropertyService) propertyService) {
this.http = http;
this.host = propertyServiceService.getProperty("API.URL"); //Is an Observable
}
getData(): Observable<any> {
return this.host.flatMap(url => this.http.get(url), (_, res) => res.json());
}
}
Upvotes: 1
Reputation: 1726
What you can do is to return properties
at observable itself and then in destination service start from that properties observable and .flatMap
a http
request after that. That means introducing more asynchronicity as was mentioned in other answers. Othr solution would be to resolve properties before app / component really start executing by retrieving properties in canActivate
router guard. In this case you can be sure that when the component calls method of other service which depends on properties being available synchronosuly the properties were already resolved.
Upvotes: 0