Как показывать счетчик, только если данные извлекаются из Http-сервиса?

#javascript #angular #typescript #http #ionic-framework

#javascript #angular #typescript #http #ionic-framework

Вопрос:

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

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

Сервис работает должным образом, но что, если я хотел бы показывать счетчик только во время http-вызова, а не при извлечении данных из кэша?

Это код моего компонента, он работает, когда getReviewsCategory (this.id ) метод моего сервиса вызывает http-сервис, но когда он извлекается из кэша, счетчик никогда не отключается. Данные загружаются корректным образом в фоновом режиме, но счетчик продолжает работать.

Метод presentLoading () находится в ngOnInit, поэтому он вызывается каждый раз, что, если я хочу вызывать его только тогда, когда данные извлекаются из кэша? Как мой компонент мог это узнать?

 
    ngOnInit() {
       this.presentLoading();
       this.CategoryCtrl();
     }

     CategoryCtrl() {
       this.serverService.getReviewsCategory(this.id)
       .subscribe((data) => {
         this.category_sources = data['value'];
         this.stopLoading();
       });
     }

     async presentLoading() {
       const loadingController = this.loadingController;

       const loadingElement = await loadingController.create({
         spinner: 'crescent',
       });
       return await loadingElement.present()
     }

     async stopLoading() {
       return await this.loadingController.dismiss();
     }

   }

  

ПРАВКА 1: это кэш-сервис:

 
    import { Injectable } from '@angular/core';

    @Injectable({
      providedIn: 'root'
    })
    export class CachingService {

      constructor() { }

      private _cache = {};

      isCashed(url: string) {
        return this._cache[url];
      }

      getData(url: string) {
        return this._cache[url];
      }

      setData(url) {
        return (data) => {
          if (data amp;amp; (data instanceof Error) === false) {
            this._cache[url] = data;
          };
        }
      }

      reset() {
        this._cache = {};
      }
    }

  

И это метод серверной службы:

 

     getReviewsCategory(cat_id) : Observable<any> {
      if (this._c.isCashed(url)) {
            return of(this._c.getData(url));
          }else{
            var modeapp = window.sessionStorage.modeapp;
            var typemodeapp = typeof(window.sessionStorage.modeapp);
            if (modeapp === "online") {
              let promise = new Promise ((resolve, reject) => {
                this.httpNative.get(url, {}, {}).
                then((data) => {
                  let mydata = JSON.parse(data.data);
                  console.log("Data from HTTP: ");
                  console.log(mydata);
                  resolve(mydata);
                }, (error) => {
                  console.log("error in HTTP");
                  reject(error.error);
               }
              );
            });
              var observable = from(promise);
          }
        }
        return observable
        .pipe(
          tap(this._c.setData(url))
        );

  

Ответ №1:

Я вижу, что вы возвращаете наблюдаемое из сервиса, вы можете попробовать следующее, чтобы посмотреть, помогает ли это.

      CategoryCtrl() {
       this.serverService.getReviewsCategory(this.id)
       .subscribe((data) => {
         this.category_sources = data['value'];
         this.stopLoading();
       },
       (error) => console.log(error),
       () => this.stopLoading(); // This always execute
     );}
  

Документы: http://reactivex.io/rxjs/class/es6/Observable.js ~ Observable.html#instance-method-subscribe

Однако я полагаю, что проблема может исходить из объекта, из которого вы вызываете .dismiss() . Вы должны вызывать dismiss для экземпляра элемента, а не для самого объекта.

 let loadingElement: Loading = null;

async presentLoading() {
   const loadingController = this.loadingController;
   this.loadingElement = await loadingController.create({
     spinner: 'crescent',
   });
   return await loadingElement.present()
}

async stopLoading() {
   return await this.loadingElement.dismiss();
}
  

Комментарии:

1. Большое спасибо… Я этого не понимаю, наблюдаемый this.serverService.getReviewsCategory(this.id ) выдает значение независимо от того, поступает ли значение из вызова службы http или из кэша, так как я могу их отличить?

2. Вам не нужно беспокоиться о них, если оба возвращаемых значения являются наблюдаемыми. Что заставляет меня думать, что, возможно, проблема в порядке операций. Получение этих значений из кэша может быть слишком быстрым, и приложение может вызывать stopLoading() код перед presentLoading() . Вы можете попробовать несколько вещей: убедитесь, что this.category_sources значение по-прежнему пустое перед вызовом create spinner и / или удалите все эти async / await из presentLoading() метода. Добавляйте только async / waitings в коде, который вы ожидаете, обещания или вы знаете, что они асинхронны

3. Вы можете проверить, откуда поступают данные, но добавление проверок для этого добавило бы больше кода и не решило бы проблему. Ваш код не должен заботиться о том, поступают ли данные из кэша или из базы данных. Кроме того, нет гарантии, что это presentLoading() завершится до CategoryCtrl() начала выполнения, поскольку это асинхронная функция. Единственный способ гарантировать это — await this.presentLoading() или, поскольку он возвращает обещание, сделать что-то вроде this.presentLoading().then(res => {this.CategoryCtrl();} .

4. Я обновил свое решение выше. Я не заметил, что это обычная конструкция в Ionic Framework, и думал, что это обычный angular.

5. Чувак, я не знаю, как тебя отблагодарить! Использование . тогда при работе this.presentLoading () счетчик отображается в течение нескольких миллисекунд, а затем отключается, в то время как если я использую http-сервис для извлечения данных, это занимает больше времени. Это не совсем то поведение, которое я искал, потому что я бы хотел вообще не показывать счетчик в случае кэша, но это, безусловно, намного лучше, чем то, что у меня было раньше!

Ответ №2:

Вы можете использовать класс HttpInterceptor для перехвата всех http-вызовов, а в методе intercept вы можете останавливать и запускать счетчик.

Вообще говоря, структура:

 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Start the spinner.
    return next.handle(req).pipe(
        map((event: HttpEvent<any>) => {
            if (event instanceof HttpResponse) {
                // Stop the spinner
            }
            return event;
        })
    );