1

I need to cache server response in my service. I checked this question caching results with angular2 http service, here I have found 2 ways of doing that 1) Observable.share() - but as it said in the answer "he share() operator works just on the first request, when all the subscriptions are served and you create another one, then it will not work, it will make another Request" 2) use ReplaySubject works nice until using resolvers (it's creating new requests). Here is my plunker https://plnkr.co/edit/TiODzGyQtXepojf4oPgw you can check network tab, both ways creating new requests when you navigate from component A to component B. Any idea how to solve this problem?

my service

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import * as Rx from 'rxjs';
import { Http } from '@angular/http';
import { ReplaySubject } from 'rxjs/ReplaySubject';

@Injectable()
export class ContactsService {
  goals: ReplaySubject<Array<any>> = new ReplaySubject(1);
  constructor(private http: Http) {}
  get() {
    if (!this.goals.observers.length) {
      return this.http
        .get('https://jsonplaceholder.typicode.com/posts/1');
    }

    return this.goals;
  }

  get2() {
    return this.http
        .get('https://jsonplaceholder.typicode.com/posts/1').share();
  }
}

Update

for subjects you can use the following method (suggested by ehrencrona)

cache: ReplaySubject<Array<any>>

get() {
  if (!this.cache) {
    this.cache = new ReplaySubject(1) 
    this.http.get(url).subscribe(this.cache)
  }

  return this.cache
}

for observable you can use this method I found out

observable: Observable<any>;
get() {
    if (!this.observable) {
      this.observable = this.http.get(url).publishReplay(1).refCount();
    }

    return this.observable;
  }

Notice that .publishReplay(1).refCount() is not the same as .share() - with .share() it't creating new requests

Victor Bredihin
  • 2,220
  • 21
  • 30
  • Please note, if you put a console.log before the http.get, it never gets called. So `!this.dataObs$.observers.length` does not seem like the best way test if the ReplaySubject has a value. It counts the number of subscribers. – Richard Matsen Nov 21 '17 at 03:37

1 Answers1

3

The standard pattern for caching a fetched value using RxJS would be as follows:

cache: ReplaySubject<Array<any>>

get() {
  if (!this.cache) {
    this.cache = new ReplaySubject(1) 
    this.http.get(url).subscribe(this.cache)
  }

  return this.cache
}

The cache instance stores the values that have been fetched. When you start out, the instance is not set, which indicates that a value has never been fetched before. When you first call get(), it will create a ReplaySubject and let it "play back" the values it gets from the AJAX call by having it subscribe to the original observable returned by http.get.

Then returned value is always the cache.

Note that this pattern solves a common problem when caching: if a second caller call the method before the first method has finished retrieving it, it will still only do the HTTP call once. If you just cached the retrieved value rather than an observable that would be hard to achieve.

ehrencrona
  • 6,102
  • 1
  • 18
  • 24
  • that looks correct, can you add a code for observable? – Victor Bredihin Nov 21 '17 at 07:43
  • get() { if (!this.observable) { this.observable = this.apiService.get('api_v3_secured_financialgoal_get'); } return this.observable; } this way creating new requests. Please check my updated plunker, so I can accept your answer – Victor Bredihin Nov 21 '17 at 07:53
  • A subject implements the observable interface, so you can return it to callers that expect an observable. There is no need for a separate implementation with a different type. – ehrencrona Nov 21 '17 at 09:00
  • yea, but I don't want to create a subject when I'm not going to update this data, it doesn't make any sense, does it? That's why I created different solution for observables – Victor Bredihin Nov 21 '17 at 09:05
  • The data needs to be stored somewhere. `ReplaySubject` is the class intended for storing and "replaying" observable sequences. So it makes sense to use it here. Other solutions are of course possible, but I don't see any disadvantages to this one. – ehrencrona Nov 21 '17 at 09:28