2017-01-09 4 views
9

У меня есть служба, которая извлекает данные через службу HTTP и возвращает наблюдаемый объект.Угловой 2 кэш наблюдаемых данных результата http

После первого вызова я хотел бы кэшировать результат внутри службы, и как только новый компонент попытается получить данные, он возьмет его из кэшированного результата.

Есть ли простое решение для этого?

+0

Wheres ваши предыдущие коды? – aimme

ответ

25

Если вы наклоняетесь в наблюдаемые в качестве средства обмена данных, вы можете принять следующий подход:

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

+0

Привет, Спасибо. Я понимаю сейчас. на самом деле я использую BehavoirSubjects и инициализировал его нулевым. Однако событие onInite не работает в службах, но это еще одна проблема (не очень импотентная). – Avi

+1

ngOnInit - это lifecycleHook. Если я не ошибаюсь, у служб нет жизненных циклов. Вместо использования ngOnInit вы должны вызвать this.fetch() в конструкторе – miniplus

+0

Привет, как реализовать хороший процесс выселения кеша с помощью планировщика? вызов функции fetch() каждый час? –

0

Мы делаем это много в нашем приложении. Как вы его реализуете, это немного зависит от того, насколько проактивно вам нужны новые ценности для распространения через ваше приложение. Ясно, что вы можете просто сохранить возвращенную коллекцию в службе как переменную-член и получить какой-либо компонент из нее, используя метод доступа. В противном случае мы хотели бы использовать RXJS BehavoirSubjects и установить в нем значение, а затем вызвать next() на нем для пропущения новых значений для всех подписчиков.

RXJS BehavoirSubject

7

Я думаю, что вы не должны делать выборку() в конструкторе или в любое время в жизненном цикле угловой. И, как вы говорите, 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; 
} 

Я не уверен, что это лучший способ, но это легче поддерживать, потому что он имеет один ResponsAbility. Данные будут кешем только в том случае, если компонент подпишет его, независимо от того, кто/кто/какой компонент нуждается в данных и является первым, в котором он нуждается.

1

Вы можете построить простой класс 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

 Смежные вопросы

  • Нет связанных вопросов^_^