#angular
Вопрос:
У меня есть компонент загрузчика, который работает с перехватчиком:
Компонент загрузчика:
import { Component } from '@angular/core';
import { LoaderService } from '../../services/loader.service';
@Component({
selector: 'app-loader',
templateUrl: './loader.component.html',
styleUrls: ['loader.component.css'],
})
export class LoaderComponent {
isLoading = this.loaderService.isLoading;
constructor(private loaderService: LoaderService) {}
}
<div class="overlay" *ngIf="isLoading | async">
<div class="lds-hourglass"></div>
</div>
Услуги погрузчика:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class LoaderService {
private _isLoading = new BehaviorSubject<boolean>(false);
isLoading = this._isLoading.asObservable();
setLoading(isLoading: boolean) {
this._isLoading.next(isLoading);
}
}
Перехватчик погрузчика:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { LoaderService } from '../services/loader.service';
@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
private totalRequests = 0;
constructor(private loaderService: LoaderService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.totalRequests ;
this.loaderService.setLoading(true);
return next.handle(req).pipe(
finalize(() => {
this.totalRequests--;
if (this.totalRequests === 0) {
this.loaderService.setLoading(false);
}
})
);
}
}
Я получаю:
Выражение изменено, если ошибка была проверена
Но я не меняю привязку после ее рендеринга, почему я получаю эту ошибку?
EDIT
If i use ngOnInit to initialize isLoading observable in Loader component, error still happening
EDIT
Finally i ended up with this solution:
Loader component:
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { LoaderService } from '../../services/loader.service';
@Component({
selector: 'app-loader',
templateUrl: './loader.component.html',
styleUrls: ['loader.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoaderComponent implements OnInit, OnDestroy {
isLoading = false;
private subs: Subscription[] = [];
constructor(private loaderService: LoaderService, private changeDetectorRef: ChangeDetectorRef) {}
ngOnInit() {
const sub = this.loaderService.isLoading.subscribe((res) => {
this.isLoading = res;
this.changeDetectorRef.detectChanges();
});
this.subs.push(sub);
}
ngOnDestroy() {
this.subs.forEach((sub) => sub.unsubscribe());
}
}
Услуги погрузчика:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class LoaderService {
isLoading = new BehaviorSubject<boolean>(false);
setLoading(isLoading: boolean) {
this.isLoading.next(isLoading);
}
}
Это работает, потому что со стратегией OnPush мы говорим, чтобы изменить цикл обнаружения для визуализации, когда мы хотим явно с this.changeDetectorRef.detectChanges();
С другой стороны, с помощью этого решения нам нужно обрабатывать подписки вручную при отключении компонента (в этом причина ngOnDestroy
).
Комментарии:
1. ‘Простое’ исправление, вероятно, состояло бы в том, чтобы обернуть это._isLoading.next(загружается); в setTimeout (с 0 временем). Может быть, это и не очень красиво, но обычно это работает нормально.
2. Да, я знаю, что это работает, потому что асинхронно, но это как-то странно