Angular Изменение темы в ngOnInit дочернего элемента, на который подписан родительский компонент, не влияет на Parent - PullRequest
0 голосов
/ 28 мая 2020

Я использую Angular 9 У меня есть родительский и дочерний компоненты и служба, в которой хранится BehaviorSubject. Все компоненты имеют стратегию OnPush

  1. Родительский компонент использует асинхронный c канал для подписки на этот объект службы
  2. Родительский компонент содержит дочерний компонент
  3. Изменения дочернего компонента этот объект службы в начале своего жизненного цикла (ngOnInit)

С этими шагами родительский элемент не будет реагировать сразу после того, как дочерний элемент изменит наблюдаемое. Вот воспроизводимая демонстрационная . Родитель не будет обновлен, пока вы, например, не сфокусируете ввод дочернего элемента.

Я отладил процесс и вижу, что функция tick вызывается после того, как asyn c pipe выполняет markForCheck. Поэтому я не могу понять, почему тогда представление не обновляется? Я заметил, что если я изменю стратегию обратно на Default, появится ExpressionChangedAfterItHasBeenCheckedError, поэтому похоже, что это изменение происходит вне основного процесса обнаружения изменений.

Вы можете мне помочь, в чем проблема с текущим подход и как правильно внести изменения, как я описал?

Ответы [ 2 ]

2 голосов
/ 28 мая 2020

Проблема в потоке данных, когда компонент OnPu sh находится в середине рендеринга, его состояние (свойства класса) должно быть стабильным, но подход в вашем примере нарушает его, потому что наблюдаемое значение было изменено в в середине рендеринга, тогда как его указатель item$ остался прежним, а для OnPu sh это не означало причин для повторного рендеринга async.

Поэтому возможные решения:

  • вручную, чтобы указать angular, что компонент был обновлен
  • , чтобы вызвать изменения, когда компонент стабилен
  • , чтобы избежать onPu sh, чтобы angular выполнила глубокую проверку для обнаружения измененного наблюдаемого значения.

вы можете принудительно проверить ngAfterContentInit родительского компонента после его контент был отрисован, и компонент стабилен.

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements AfterContentInit {
  constructor(private iService: ItemsService, private cdr: ChangeDetectorRef) { }
    item$ = this.iService.item$;

    ngAfterContentInit() { // <- add this
      this.cdr.detectChanges();
      this.cdr.markForCheck();
    }
}

Вот демонстрация: https://stackblitz.com/edit/angular-ivy-pxxiee?file=src%2Fapp%2Fcomponents%2Finner%2Finner.component.ts


Если вы не хотите использовать ngAfterContentInit, тогда вы можете отложить эмиссии, чтобы они были выпущены после всех хуков жизненного цикла.

    item$ = this.itemSubject.asObservable().pipe(
      delay(0),
    );

Вот демонстрация: https://stackblitz.com/edit/angular-ivy-v7upxo?file=src / app / services / items.service.ts

0 голосов
/ 28 мая 2020

Эта проблема возникает из-за того, что родительский компонент нуждался в дочернем компоненте для инициализации свойства, прежде чем он сможет пометить свое представление как инициализированное. Прежде чем инициализация родителя могла быть завершена, вы изменили значение, отображаемое в шаблоне. Вот почему, когда вы переходите к стратегии по умолчанию, вы получаете ошибку выражения.

Чтобы обойти это, поместите оператор изменения значения, который находится в ngOnInit, в setTimeout, чтобы первый родитель мог завершить свою инициализацию, а затем вы испустите это value.

Примечание: - Я говорю setTimeout только для наблюдения за поведением. Не как решение.

...