3

我遇到了这样的代码。问题是在选择已经在缓存中的项目后进度条没有消失(当缓存内的 api 调用正常工作时)。我能够想出的是在点击执行操作后没有运行更改检测。有人可以向我解释为什么吗?

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

编辑:

所以我想:tap 正在作为异步操作运行,这就是为什么在组件上已经运行更改检测之后执行它的原因。像这样的东西:

  1. this.isProgressBar = true;
  2. 变更检测运行
  3. 点击(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() 中进行操作后更改检测没有在组件上运行,它与区域有关吗?

物品服务:

private progressBar = new Subject<boolean>();

    setProgressBar(value: boolean) {
        this.progressBar.next(value);
    }

    get isProgressBar$() {
        return this.progressBar.asObservable();
    }
4

1 回答 1

2

对我来说,您的代码有两个主要问题:

  • 您的缓存可能不会发出新值(我不知道,因为您没有提供它的实现),这意味着async管道不会被触发,

  • 因为您检测到onPush,您的视图没有被任何东西刷新:只有您触摸的方法/属性正在更新。与您的进度条无关,除非您使用它(这会触发组件更改检测),item$否则您不会看到它被更新。detectChanges

于 2018-12-07T08:44:08.660 回答