在Angular 4中从HttpClient缓存数据

时间:2017-09-26 01:45:37

标签: angular caching angular-http angular-http-interceptors

我有一个问题,让我的缓存更简单。我认为有更好的方法来做到这一点。我的问题是我必须这样做"缓存"每个get()函数中的代码都会产生更长的代码。有谁帮助如何做到这一点最好的方式?谢谢。这是我的代码,如下所示。我在我的代码中做的是我在news.service.ts中执行get()函数以从http获取数据,并在我的新闻列表中订阅它。

  

news.service.ts

getAllNews() {

    if(this.newslist != null) {
      return Observable.of(this.newslist);
    } 

    else {

      return this.httpClient
        .get('http://sample.com/news')
        .map((response => response))
        .do(newslist => this.newslist = newslist)
        .catch(e => {
            if (e.status === 401) {
                return Observable.throw('Unauthorized');           
            }

        });
    }
  }
  

新闻list.service.ts

 this.subscription = this.newsService.getAllNews()
      .subscribe(
        (data:any) => {
          console.log(data);
          this.newslists = data.data.data;
        },
        error => {
          this.authService.logout()
          this.router.navigate(['signin']);
        });
  }

2 个答案:

答案 0 :(得分:2)

如果您打算使用通用的,可以用于不同的API调用或服务,那么您可以执行以下操作:

import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';

class CacheItem<T> {
  url: string;
  timestampCached: number;
  data: T;
}

@Injectable({
  providedIn: 'root'
})
export class MyCachedHttpClient {
  cache: CacheItem<any>[] = [];

  constructor(
    private http: HttpClient,
  ) { }

  get<T>(url: string, cacheTime?: number, forceRefresh: boolean = false)
  : Observable<T> {
    let cachedItem: CacheItem<T> = this.getCachedItem<T>(url);

    if (cachedItem != undefined && !forceRefresh) {
      let expireDate = cachedItem.timestampCached + cacheTime;
      if (Date.now() < expireDate) {
        return of(cachedItem.data);
      }
    }

    return this.http.get<T>(url).pipe(
      map(data => {
        if (cacheTime) { // if we actually want to cache the result
          if (cachedItem == undefined) {
            cachedItem = new CacheItem();
            cachedItem.url = url;
            this.cache.push(cachedItem);
          }
          cachedItem.data = data;
          cachedItem.timestampCached = Date.now();
        }
        return data;
      })
    );
  }

  private getCachedItem<T>(url: string): CacheItem<T> {
    return this.cache.find(item => item.url == url);
  }
}

然后在任何地方使用MyCachedHttpClient代替HttpClient

注意:

  • 这适用于Angular 6 / RxJS 6.如果您在下面,请参阅编辑历史记录中的代码。
  • 这只是隐藏HttpClient get()函数的许多功能的基本实现,因为我没有在此重新实现options参数。

答案 1 :(得分:1)

我不太清楚news.service.tsnews-list.service.ts之间的区别是什么,但主要的概念是你需要分开关注点。您可以做的最基本的分离是将“数据提供者”从“数据使用者”中分离出来

数据提供者

这可以是获取和管理数据的任何内容。无论是内存缓存数据,服务调用等。在您的示例中,在我看来news.service.ts它是与新闻相关的所有内容的Web API客户端/代理。

如果这是一个小文件,您可以将所有与新闻相关的缓存管理移动到此文件或...创建另一个管理缓存和包装news.service.ts的组件。该组件将从其缓存中提供数据,如果缓存不存在或已过期,则它会调用news.service.ts方法。这种方式news.service.ts唯一的责任就是向API发出ajax请求。

这是一个没有承诺,可观察或错误处理的例子,只是为了给你一个想法......

class NewsProvider{

    constructor(){
        this.newsCache = [];
    }

    getNewsItemById(id){
        const item = this.newsCache.filter((i) => {i.id === id});

        if(item.length === 1) return item[0];

        this.newsCache = newsService.getAllNews();

        item = this.newsCache.filter((i) => {i.id === id});

        if(item.length === 1) return item[0];
        else return null;
    }
}

数据使用者

这些将是需要数据的任何组件,主页中的新闻自动收录器,导航菜单中某处的徽章通知......可能存在需要新闻相关数据的任何组件(或视图)。出于这个原因,这些组件/视图不需要知道数据来自何处。

这些组件将使用“数据提供者”作为主要数据来源

摘要

只需要在一个地方管理(并且可以)管理缓存以及与网络相关的操作