Angular 2 cache observable http result data [duplicate]

无人久伴 提交于 2019-12-17 05:55:32

问题


I have a service that fetches data via the HTTP service and returns an observable object.

After the first call I would like to cache the result internally in the service, and once a new component will try to get the data it will take it from the cached result.

Is there a simple solution for this?


回答1:


If you lean into observables as a means of sharing data, you can adopt the following approach:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import { Observable, ReplaySubject } from 'rxjs';

@Injectable()
export class CachedService {
  data$: Observable<Response> = this.dataSubject.asObservable();

  private dataSubject = new ReplaySubject<Response>(1);

  constructor(private http: Http) { }

  fetch() {
    this.http.get(...).subscribe(res => this.dataSubject.next(res));
  }
}

This will make an HTTP call when the fetch method is called, and any subscribers to service.data$ will get the response from the ReplaySubject. As it replays earlier values, any subscribers who join after the HTTP call resolves will still get the previous response.

If you want to trigger an update, you can just call service.fetch() to kick off a new HTTP call and all subscribers will be updated once the new response arrives.

Your components would then look something like:

@Component({ ... })
export class SomeComponent implements OnInit {

  constructor(private service: CachedService) { }

  ngOnInit() {
    this.service.fetch();
    this.service.data$.subscribe(...);
  }
}

I've recently written a blog article on this approach for my colleagues: http://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html




回答2:


I think you should not do a fetch() in the constructor or any time in the lifecycle of angular. And as you say, ngOnInit does not work in angular services.

Instead we want to leverage rxjs to seamlessly pass us cached values through the stream – without the caller having to know anything about cached vs non cached values.

If a component needs a data, it subscribes to it, regardless if it is cache or not. Why would you fetch() a data that you are not sure it will be used ?

Cache should be implement at a higher level. I think this kind of implementation is a good start : http://www.syntaxsuccess.com/viewarticle/caching-with-rxjs-observables-in-angular-2.0

getFriends(){
    if(!this._friends){
      this._friends = this._http.get('./components/rxjs-caching/friends.json')
                                   .map((res:Response) => res.json().friends)
                                   .publishReplay(1)
                                   .refCount();
    }
    return this._friends;
}

I am not sure it is the best way, but it is easier to maintain because it has a single responsability. Data would be cache only if a component subcribes to it, no matter what/who/which component needs the data and is the first to need it.




回答3:


You can build simple class Cacheable<> that helps managing cache of data retrieved from http server or other any other source:

declare type GetDataHandler<T> = () => Observable<T>;

export class Cacheable<T> {

    protected data: T;
    protected subjectData: Subject<T>;
    protected observableData: Observable<T>;
    public getHandler: GetDataHandler<T>;

    constructor() {
      this.subjectData = new ReplaySubject(1);
      this.observableData = this.subjectData.asObservable();
    }

    public getData(): Observable<T> {
      if (!this.getHandler) {
        throw new Error("getHandler is not defined");
      }
      if (!this.data) {
        this.getHandler().map((r: T) => {
          this.data = r;
          return r;
        }).subscribe(
          result => this.subjectData.next(result),
          err => this.subjectData.error(err)
        );
      }
      return this.observableData;
    }

    public resetCache(): void {
      this.data = null;
    }

    public refresh(): void {
      this.resetCache();
      this.getData();
    }

}

Usage

Declare Cacheable<> object (presumably as part of the service):

list: Cacheable<string> = new Cacheable<string>();

and handler:

this.list.getHandler = () => {
// get data from server
return this.http.get(url)
.map((r: Response) => r.json() as string[]);
}

Call from a component:

//gets data from server
List.getData().subscribe(…)

More details and code example are here: http://devinstance.net/articles/20171021/rxjs-cacheable



来源:https://stackoverflow.com/questions/41554156/angular-2-cache-observable-http-result-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!