Angular 2 кеш наблюдаемых данных http результатов
У меня есть служба, которая извлекает данные через службу HTTP и возвращает наблюдаемый объект.
После первого вызова я хотел бы кэшировать результат внутри службы, и, как только новый компонент попытается получить данные, он возьмет их из кэшированного результата.
Есть ли простое решение для этого?
3 ответа
Если вы используете наблюдаемые в качестве средства обмена данными, вы можете использовать следующий подход:
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));
}
}
Это сделает HTTP-вызов, когда fetch
метод вызывается, и любые подписчики service.data$
получит ответ от ReplaySubject
, Поскольку он воспроизводит более ранние значения, все подписчики, которые присоединятся после разрешения HTTP-вызова, все равно получат предыдущий ответ.
Если вы хотите запустить обновление, вы можете просто позвонить service.fetch()
инициировать новый HTTP-вызов, и все подписчики будут обновлены после получения нового ответа.
Ваши компоненты будут выглядеть примерно так:
@Component({ ... })
export class SomeComponent implements OnInit {
constructor(private service: CachedService) { }
ngOnInit() {
this.service.fetch();
this.service.data$.subscribe(...);
}
}
Я недавно написал статью в блоге об этом подходе для моих коллег: http://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html
Я думаю, что вы не должны делать fetch() в конструкторе или в любое другое время жизненного цикла angular. И, как вы говорите, ngOnInit
не работает в угловых сервисах.
Вместо этого мы хотим использовать rxjs, чтобы беспрепятственно передавать нам кэшированные значения через поток - без необходимости, чтобы вызывающая сторона знала что-либо о кэшированных и не кэшированных значениях.
Если компоненту нужны данные, он подписывается на них, независимо от того, кеш это или нет. Зачем вам получать () данные, которые вы не уверены, что они будут использоваться?
Кэш должен быть реализован на более высоком уровне. Я думаю, что такого рода реализация является хорошим началом: 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;
}
Я не уверен, что это лучший способ, но его легче поддерживать, потому что он имеет единственную ответственность. Данные будут кешироваться только в том случае, если компонент будет им подчиняться, независимо от того, кому / кому / какому компоненту нужны данные, и они ему нужны первыми.
Вы можете создать простой класс Cacheable<>, который помогает управлять кэшем данных, полученных с http-сервера или из любого другого источника:
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();
}
}
использование
Объявите объект Cacheable<> (предположительно как часть службы):
list: Cacheable<string> = new Cacheable<string>();
и обработчик:
this.list.getHandler = () => {
// get data from server
return this.http.get(url)
.map((r: Response) => r.json() as string[]);
}
Звонок из компонента:
//gets data from server
List.getData().subscribe(…)
Более подробная информация и пример кода находятся здесь: http://devinstance.net/articles/20171021/rxjs-cacheable