web-dev-qa-db-fra.com

Mise en cache des données de HttpClient dans Angular 4

j'ai un problème pour rendre ma mise en cache plus simple. Je pense qu'il y a une meilleure façon de faire cela. Mon problème est que je dois faire cette "mise en cache" des codes dans chaque fonction get (), ce qui entraîne des codes plus longs. Quelqu'un aide-t-il à faire cela de la meilleure façon? Merci. Voici mes codes ci-dessous. Ce que j'ai fait dans mes codes, c'est que je fais la fonction get () dans mon news.service.ts pour obtenir des données à partir du http et que je les abonne dans ma liste de discussion.

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');           
            }

        });
    }
  }

news-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']);
        });
  }
6
Joseph

Si vous vouliez avoir quelque chose de générique, que vous pouvez utiliser pour différents appels ou services d'API, vous pouvez faire quelque chose comme ceci:

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);
  }
}

Et puis, utilisez simplement MyCachedHttpClient au lieu de HttpClient partout.

Remarques:

  • Ceci est pour Angular 6/RxJS 6. Voir le code de Edit History si vous êtes en dessous.
  • Ceci est juste une implémentation de base qui cache de nombreuses fonctionnalités de la fonction get() de HttpClient puisque je n’ai pas réimplémenté le paramètre options ici.
2
MA-Maddin

Je ne sais pas trop quelle est la différence entre news.service.ts et news-list.service.ts, mais le concept principal est que vous devez séparer les préoccupations. La séparation la plus élémentaire que vous puissiez faire consiste à distinguer les "fournisseurs de données" des "consommateurs de données"

Fournisseurs de données

Cela pourrait être tout ce qui va chercher et gérer les données. Qu'il s'agisse de données en mémoire cache, d'un appel de service, etc. Dans votre exemple, il me semble que news.service.ts est un client/proxy d'API Web pour tout ce qui a trait aux actualités. 

S'il s'agit d'un petit fichier, vous pouvez déplacer toute la gestion du cache liée aux nouvelles vers ce fichier ou ... créer un autre composant qui gère le cache et encapsule news.service.ts. Ce composant servirait les données à partir de son cache. Si le cache n'existe pas ou a expiré, il fait appel à des méthodes news.service.ts. De cette façon, la seule responsabilité de news.service.ts est de faire des requêtes ajax à l'API.

Voici un exemple sans promesse, sans observables ni gestion des erreurs, juste pour vous donner une idée ...

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;
    }
}

Consommateurs de données

Il s’agit de tout composant nécessitant des données, d’un ticker d’actualités sur la page d’accueil, d’une notification de badge quelque part dans le menu de navigation, etc. Pour cette raison, ces composants/vues ne devraient pas avoir besoin de savoir d'où proviennent ces données.

Ces composants utiliseront des "fournisseurs de données" en tant que source principale de données

Résumé

Le cache doit uniquement être (et peut être) géré à un seul endroit, ainsi que par les opérations liées au réseau

1
Leo