Связь с шаблоном angular задерживается на один тик

Я играл с ng-template и обнаружил, что борюсь с какой-то задержкой.

Я создал один простой пример в stackblitz. https://stackblitz.com/edit/angular-template-series-v?file=app%2Fparent%2Fparent1.component.ts

Родительский компонент отправляет компонент Hello своему Дочернему с помощью шаблонов.

Идея заключалась в том, чтобы управлять шаблоном из дочернего элемента с помощью ngIf.

Когда компонент Hello достигает цикла ловушки ngOnInit, он генерирует выходные данные. Родительский компонент получает этот вывод и добавляет сообщение для отображения.

Но сообщение не отображается, когда компонент создается, а не на один цикл позже. В этом примере вам нужно дважды щелкнуть кнопку «Переключить содержимое», чтобы появилось сообщение «Шаблон создан».

Как я могу решить эту проблему, чтобы сообщение появлялось в том же цикле?


person Adrian Abreu    schedule 28.03.2018    source источник
comment
Вы можете иметь такое же поведение без явного использования ng-template: stackblitz.com/edit/ Just Child - ›общение с дедушкой и бабушкой задерживается   -  person David    schedule 09.04.2018


Ответы (3)


Это связано с тем, как обнаружение изменений работает в Angular. Это не связано с использованием ng-template, так как это измененное отображение stackblitz (с использованием grand child без использования ng-template).

2 вещи, которые нужно знать об обнаружении изменений:

  • он запускается после событий (щелчок, отправка, ..), запросов xhr или таймеров.
  • это происходит сверху вниз по дереву компонентов

Обычно, когда вы нажимаете кнопку переключения:

  • обработчик запускается (переключение значения showContent в вашем примере, но ничего больше. Компонент hello НЕ создается на этом этапе)
  • срабатывает обнаружение изменений, всегда начиная сверху вниз.
  • angular начинается с проверки необходимости обновления представления для родительского компонента. Поскольку еще ничего не изменилось, родительский вид не обновляется.
  • Затем дочерний компонент обнаруживает, что его showContentValue изменились. Это вызывает создание компонента hello.
  • После инициализации компонента hello выдается событие created. Родительский компонент получил это событие и соответственно обновляет массив сообщений. Но на этом этапе родительский контент уже проверен, поэтому Angular не будет вносить никаких изменений в родительский вид.

Когда вы снова нажимаете кнопку переключения, тот же цикл запускается снова, и на этот раз отображается сообщение, помещенное в массив в предыдущем цикле.

Вот измененный пример stackblitz с журналами для хуков жизненного цикла

Примечание

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

person David    schedule 09.04.2018

Это потому, что вы нарушаете цикл обнаружения изменений Angular.

Вот что происходит после того, как вы нажмете первую кнопку-переключатель:

1- Вы нажимаете на переключатель

2- ZoneJS, у которого есть хуки в событиях кликов, получает уведомление (проще говоря)

3- Zone гарантирует, что событие щелчка завершено

4- Zone сообщит Angular, что произошло событие async, мы должны проверить компонент и убедиться, что представление (DOM) отражает модель (модель / мир javascript). Итак, change detection запустится, обнаружит все изменения и обнаружит, что в шаблоне есть привязки (ng-if).

5- После того, как ngIf станет истинным, И ЗАМЕТЬТЕ, ЧТО ЭТО ВСЕ СИНХРОНИЗАЦИЯ ТАК ДАЛЕКО, создается ng-container, создается ваш templateOutlet, а ваш hello компонент будет визуализирован.

ОБРАТИТЕ ВНИМАНИЕ: все это синхронизируется.

6 - Просмотр был обновлен, обнаружение изменений отключено.

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

8- Массив теперь обновлен, а представление - нет (в родительском компоненте)

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

Чтобы доказать свою правоту: D:

 onTemplateCreated() {
    console.log('Got the event from chil');
   this.template.push('Template created');
    console.log('this.template',this.template);
  }

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

Теперь перейдем к моему коду

 onTemplateCreated() {
    console.log('Got the event from chil');
   this.template = [...this.template,'Template created'];
  }

BE потому что я не мутирую, и ссылка обновляется, Angular теперь должен позаботиться об этом изменении, НО проблема все еще существует, детектор изменений Angular завершен, и вы обновили модель без запуска какого-либо события async , так что у нас проблемы, потому что Angular не хочет, чтобы модель обновлялась после завершения обнаружения изменений,

Таким образом, вы получите сообщение об ошибке:

ExpressionChangedAfterItHasBeenCheckedError: выражение изменилось после проверки. Предыдущее значение

Чтобы исправить это, сначала прочтите мой другой пост, чтобы понять его , во-вторых, вам нужно сообщить Angular, что вы знаете, что делаете, и запустить обнаружение изменений вручную:

  this._cd.detectChanges

ИЛИ вы можете сделать это асинхронным:

onTemplateCreated() {
    console.log('Got the event from chil');
   setTimeout(()=>{
      this.template = [...this.template,'Template created'];
   })
  }
person Milad    schedule 09.04.2018

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

Вот пример: stackblitz

person Alexander Poshtaruk    schedule 05.04.2018
comment
Спасибо за ответ. Если я запустил changeDetector, я не просто ищу новый цикл? Я хотел бы знать, почему существует несоответствие цикла между выводом и представлением. - person Adrian Abreu; 09.04.2018
comment
Я думаю, когда вы просто вставляете компонент с ng-content - это занимает одну галочку. Но если вы вставляете шаблон, тогда ngOutlet создает представление с дочерним компонентом - это может занять больше тиков, и, возможно, обновления родительских свойств в этом случае выполняются после цикла changeDetection. Так что нужно активировать его дополнительно - person Alexander Poshtaruk; 09.04.2018
comment
Вы можете попробовать поместить шаблон отдельно и передать его дочернему элементу в качестве входного параметра. Может и поможет, но не уверен - person Alexander Poshtaruk; 09.04.2018