я сталкивался с таким кодом.Проблема заключалась в том, что индикатор выполнения не исчезал после выбора элемента, который уже находился в кэше (когда был выполнен вызов API внутри кэша, он работает нормально).Я смог придумать, что обнаружение изменений не запускается после выполнения операции в tap.Может кто-нибудь объяснить мне, почему?
@Component({
selector: 'app-item',
templateUrl: `
<app-progress-bar
[isDisplayed]="isProgressBar"
></app-progress-bar>
<app-item-content
[item]="item$ | async"
></app-item-content>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemComponent {
@Input()
set currentItemId(currentItemId: string) {
if (currentItemId) {
this.isProgressBar = true;
this.item$ = this.cache.get(currentItemId).pipe(
tap(() => {
this.isProgressBar = false;
//adding this.changeDetector.detectChanges(); here makes it work
})
);
} else {
this.isProgressBar = false;
this.item$ = of(null);
}
}
item$: Observable<ItemDto>;
isProgressBar = false;
constructor(
private cache: Cache,
private changeDetector: ChangeDetectorRef
) {}
}
Кэш хранит элементы в private _data: Map<string, BehaviorSubject<ItemDto>>;
и отфильтровывает исходный нулевой выброс
, также изменяя
<app-progress-bar
[isDisplayed]="isProgressBar"
></app-progress-bar>
на
<app-progress-bar
*ngIf="isProgressBar"
></app-progress-bar>
заставляет его работать без ручного запуска обнаружения изменений, почему?
Кэш:
export class Cache {
private data: Map<string, BehaviorSubject<ItemDto>>;
get(id: string): Observable<ItemDto> {
if (!this.data.has(id)) {
this.data.set(id, new BehaviorSubject<ItemDto>(null));
}
return this.data.get(id).asObservable().pipe(
tap(d => {
if (!d) {
this.load(id);
}
}),
filter(v => v !== null)
);
}
private load(id: string) {
this.api.get(id).take(1).subscribe(d => this.data.get(id).next(d));
}
Редактировать:
Итак, я подумал: тап выполняется как асинхронная операция, поэтому он выполняетсяпосле обнаружения изменений уже запущен компонент.Примерно так:
- this.isProgressBar = true;
- запуск обнаружения изменений
- tap (this.isProgressBar = false;)
Но я возился с этим и сделал что-то вроде этого:
templateUrl: `
<app-progress-bar
[isDisplayed]="isProgressBar$ | async"
></app-progress-bar>
<app-item-content
[item]="item$ | async"
></app-item-content>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemComponent {
@Input()
set currentItemId(currentItemId: string) {
if (currentItemId) {
this.itemService.setProgressBar(true);
this.item$ = this.cache.get(currentItemId).pipe(
tap(() => {
this.itemService.setProgressBar(false);
})
);
} else {
this.itemService.setProgressBar(false);
this.item$ = of(null);
}
}
item$: Observable<ItemDto>;
isProgressBar$ = this.itemService.isProgressBar$;
И теперь я понятия не имею, почему после выполнения операции в tap () обнаружение изменений не запускается на компоненте, есть ли у него что-тоделать с зонами?
ItemService:
private progressBar = new Subject<boolean>();
setProgressBar(value: boolean) {
this.progressBar.next(value);
}
get isProgressBar$() {
return this.progressBar.asObservable();
}