Угловая асинхронная труба не запускает обнаружение изменений из NgOnChange - PullRequest
0 голосов
/ 07 декабря 2018

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

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

Итак, я подумал: тап выполняется как асинхронная операция, поэтому он выполняетсяпосле обнаружения изменений уже запущен компонент.Примерно так:

  1. this.isProgressBar = true;
  2. запуск обнаружения изменений
  3. 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();
    }

1 Ответ

0 голосов
/ 07 декабря 2018

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

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

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

...