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