我遇到了这样的代码。问题是,选择已在缓存中的项目后,进度条没有消失(当使缓存内的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>>;
,并且正在过滤掉初始null发射
也在更改
<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));
}
编辑:
所以我想到:tap正在作为异步操作运行,这就是为什么在组件上已经执行了更改检测后才执行tap的原因。像这样:
但是我对此很摆弄,做了这样的事情:
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();
}
答案 0 :(得分:2)
对我来说,您的代码有两个主要问题:
您的缓存可能不会发出新值(我不知道,因为您没有提供它的实现),这意味着async
管道不会被触发,
因为您检测到onPush
,所以您的视图不会被任何事物刷新:只有您触摸的方法/属性才会被更新。 item$
与进度条无关,使用detectChanges
不会看到它正在更新(这会触发组件更改检测)。