Thomas Stubbe
Thomas Stubbe

Reputation: 1985

RxJS and angular2

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

Answers (2)

paulpdaniels
paulpdaniels

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

tomastrajan
tomastrajan

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

Related Questions