Угловая асинхронная труба не запускает обнаружение изменений из NgOnChange

Я сталкивался с таким кодом. Проблема заключалась в том, что индикатор выполнения не исчезал после выбора элемента, который уже находился в кэше (когда был сделан вызов 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));
}

Редактировать:

Итак, я понял: 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() обнаружение изменений не запускается на компоненте, имеет ли оно какое-то отношение к зонам?

ItemService:

private progressBar = new Subject<boolean>();

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

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

1 ответ

Для меня есть два основных вопроса с вашим кодом:

  • Ваш кеш, вероятно, не генерирует новые значения (чего я не знаю, потому что вы не предоставили его реализацию), то есть async труба не срабатывает,

  • потому что вы обнаруживаете onPushваш взгляд ничем не обновляется: обновляются только те методы / свойства, к которым вы прикоснулись. item$ не связаны с вашим индикатором выполнения, вы не увидите, что он обновляется, если вы используете detectChanges (который запускает обнаружение изменения компонента).

Другие вопросы по тегам