Изменить вход компонента из ngOnChanges, используя стратегию OnPu sh - PullRequest
2 голосов
/ 27 февраля 2020

У меня проблема с приложением Angular 6:

Проблема

Допустим, у меня есть 2 компонента: родительский и дочерний.

дочерний имеет 2 входа. Пока изменяется 1 вход, внутри ngOnChanges () дочерний компонент что-то передает родительскому компоненту.

Затем родительский компонент изменяет второй вход для дочернего компонента. Я ожидаю, что обнаружение изменений дочернего компонента будет вызвано еще раз, НО ЭТО НЕ. Представление дочернего компонента показывает старое значение второго ввода.


Определения

Приложение имеет следующие определения:

  1. Все компоненты используют изменения стратегия обнаружения OnPu sh. это включает в себя root компонент.
  2. Я использую ngrx store. call markForCheck () или detectChanges () внутри редуктора не является опцией.
  3. Данные передаются дочернему компоненту по асинхронному каналу c, поскольку поступают из хранилища с помощью селектора (наблюдаемого).
  4. Я не могу вызвать изменение второго ввода ранее (например, в родительском компоненте) - это должно произойти после того, как дочерний компонент получит изменение первого ввода.

Чтобы проиллюстрировать это, я создал простой демонстрационный проект. Там я не использую store, поэтому у меня нет селекторов ... вместо этого я использую rx js Тема для использования asyn c pipe.

Вот оно:

https://angular-ccejq4.stackblitz.io

Посмотрите на консоль, чтобы понять, что происходит и когда.


Плохие решения

Что я уже пробовал:

  1. изменить стратегию обнаружения изменений в компоненте root по умолчанию. Это работает, но это не очень хорошее решение, так как вызывает проблемы с производительностью.
  2. call markForCheck () до и после emit (!) - НЕ РАБОТАЕТ.
  3. call detectchanges () до и после emit (!) - НЕ РАБОТАЕТ.
  4. подписывается на селектор в родительском компоненте вместо использования asyn c pipe - НЕ РАБОТАЕТ.
  5. вызывает emit из setTimeout - IT РАБОТАЕТ. но есть ли лучшее решение? более Angular управляемый. потому что это решение больше похоже на обходной путь.
  6. используйте ngAfterViewInit / ngAfterViewChecked и emit, оттуда вызывайте markForCheck () и detectChanges () - НЕ РАБОТАЮ.

Я обнаружил, что новый значение поступает в асинхронную c трубу. канал asyn c затем вызывает markForCheck (). но он не вызывает detectChanges () .. поэтому цикл обнаружения изменений не будет запущен. вид изменится правильно только при следующем цикле обнаружения изменений - как вы можете видеть в моем демонстрационном приложении.


Есть идеи?

спасибо!

1 Ответ

0 голосов
/ 27 февраля 2020

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

Из ngOnChanges нет прямого способа вызвать другое обнаружение изменений. В противном случае у вас будет возможность поразить всех oop. Кроме того, производительность не позволяет этого. Вам нужно найти способ попасть в следующий цикл выполнения JS

. setTimeout - неплохой вариант для преодоления этого. Вы можете поместить это вокруг эмиссии:

setTimeout(() => this.secondEmitter.emit(this.first));

или вы можете поместить это вокруг markForCheck вызова:

setTimeout(() => this.cdRef.markForCheck());

Но я согласен с вами, что это чувствует немного бестолково, но иногда вам просто нужно с этим справиться. Если вы действительно не можете, есть другой способ. Ваш BehaviorSubject по своей природе является синхронной наблюдаемой. Однако вы можете сделать подписку async в своем шаблоне, чтобы использовать asyncScheduler:

private second = new BehaviorSubject(0);

readonly second$ = this.second.asObservable().pipe(
  observeOn(asyncScheduler)
)

constructor() {
  this.second.next(0);
  this.second$.subscribe((lastval) => {
    console.log(`second subscription update to ${lastval}`);
  });
}

Из последнего варианта, который я сделал, получается ваш стек

.

При этом любая подписка на него получит сообщение после следующего цикла l oop.

...