1

I'm with Angular 2 and RxJS and I'm having a hard time setting up a simple observables system.

As far as I understand, operator do is used for side effects, and you place the code to deal with the results returned by the observable in the susbcribe() function.

So my Component ask the Service to initialize the system. The Service makes 2 http calls to the server and combines them both in a single stream for the component to subscribe and make sure everything is ready. One of the 2 http calls in the service is retrieving a fixed number of results from the server, which will be then served to the Component on demand. So when the Service sees that it needs more data, it makes another call to the server.

This was not working because I think I need to unsubscribe first to the original http call before making a new one (is that true?).

So this is what I've got... which is not working because it not getting to subscribe in the getQuestions method.

In the Component

ngOnInit() {
    // show spinner

    this.gamesService
      .initializeGame()
      .subscribe(data => {
        console.log('Game initialized');
        // remove spinner
      });
  }

In the Service

initializeGame(opponent_id: string): Observable<any> {
    return Observable
      .forkJoin([this.getQuestions(), this.newGame()]);
  }

  getQuestions(): Observable<any> {
    // composes postData with POST parameters

    this.questionsObservable = this.authHttp
      .post(this.settings.authApiUrl, postData)
      .map((response: Response) => response.json());

    this.questionsObservable
      .subscribe(data => {
        // save data in the service, which will be consumed
      })
      .unsubscribe(); // do I need to unsubscribe?

    return this.questionsObservable;
  }

  getQuestion(category: string): any {
    // consume question by Component
    if (no_more_questions_in_service) this.getQuestions();
    return question;
  }

So, this is now working. Why is not reaching the subscribe method?

Is there a better way to achieve this? Thanks

David
  • 3,364
  • 10
  • 41
  • 84
  • 2
    You unsubscribe with `.unsubscribe()` before the `authHttp` service is able to emit any item. – martin Jan 27 '17 at 10:29

1 Answers1

2

Ok, I realize (also, thanks @martin) I was unsubscribing before time.

I managed to get what I wanted, so in case someone wants to use it.

The service is now like this:

initializeGame(opponent_id: string): Observable<any> {
    return Observable
      .forkJoin([this.getQuestions(), this.newGame()]);
  }

  getQuestions(): Observable<any> {
    // postData

    return this.authHttp
      .post(this.settings.authApiUrl, postData)
      .map((response: Response) => response.json())
      .do(data => save_data);
  }

  private getQuestionsByCategory(category: string) {
    // postData

    this.authHttp
      .post(this.settings.authApiUrl, postData)
      .map((response: Response) => response.json())
      .subscribe(data => save_data
  }

  getQuestion(category: string): any {
    // consume question by Component
    if (no_more_questions_in_service)
      this.getQuestionsByCategory(category);
    }
    return question;
  }

So I have 2 different methods now; I don´t need to unsubscribe (I think) and it seems to be working fine now.

David
  • 3,364
  • 10
  • 41
  • 84
  • As a general rule, the service itself should NOT subscribe to the observables it wraps. Instead, the **consumer** of the service should. In your case, the component should subscribe, not the service. More specifically, I don't understand why you have a `subscribe()` in `getQuestionsByCategory()`. It looks like you're not doing anything with the data this subscribe receives. – AngularChef Jan 27 '17 at 11:49
  • Thanks @AngularFrance, in getQuestionsByCategory() whay I'm doing is store the data that I get from the server locally in my service. In this case, should I use do() instead of subscribe()? I understand that the component should be the one subscribing, but in this case the component does not use all data at once, it only needs one item at a time and it asks the service every time it needs a new one. – David Jan 27 '17 at 11:58
  • So you want your service to cache the data? Two scenarios then: 1) you can either cache that data (as a private property on the service) the first time **the component** subscribes; then on subsequent calls, you check whether the data already exists before re-executing the observable. 2) OR, you want to eagerly populate the cache, even before the component subscribes. In that case, I agree that you have to have a subscribe somewhere, but still I wouldn't leave it inside the service. – AngularChef Jan 27 '17 at 12:06
  • [continued] Imagine that one day you write unit tests for this service. It'll be a pain in the butt to test if you have methods that "auto-subscribe". Better expose the obs to the outside world (in that case, your tests) and let the outside call it. Anyway, our convo is a bit abstract and I don't know the specifics of YOUR project. :) – AngularChef Jan 27 '17 at 12:08
  • Ok, the 1st scenario is what I'm doing; my component subscribes to the first call to make sure that the data is in the service. So the service is caching the data in a private property. Then, the component asks for data to the service, which serves it from the cached data (basic array and shift()), and when realizes that it has no more data, then it fetches more with getQuestionsByCategory()). But in this case, the Component does not know anything about it, the data is used only by the service, so I don´t really see the need to subscribe outside the service :( – David Jan 27 '17 at 13:08