Компонент Angular2: невозможно создать @Eutput EventEmitter внутри ngOnDestroy?

[angular2 rc1]

Можно ли иметь такой компонент:

export class MyComp {
  @Output() myEvent = new EventEmitter(false)

  ngOnDestroy() {
    this.myEvent.emit('ngOnDestroy hook');
  }
}

И поймать это в родителях:

<myComp (myEvent)="test($event)"></myComp>

Это кажется невозможным, но я хотел бы понять, почему?

Я знаю, что могу использовать сервис, чтобы пройти.

Плункер здесь

2 ответа

Решение

Сервис - это хорошее решение, но вы хотели понять, почему:

Объяснение TL/DR:

Подписка для вас отписана, под углом до вызова ngOnDestroy(). Эта автоматизация происходит потому, что вы подписаны на шаблон, angular анализирует шаблон и знает достаточно для этого.

Более подробное объяснение:

Angular генерирует фабрику для каждого компонента, этот процесс называется генерацией кода, а функция фабрики, созданная angular, возвращает экземпляр уникального типа View, созданного в соответствии с метаданными вашего компонента, который наследует класс AppView.

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

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

Это делается статически, что означает, что генератор кода знает о подписке на события и поэтому добавляет жестко закодированную логику для ее удаления. Важно то, что это экономит много стандартного кода и предотвращает утечки памяти - фреймворк очищает для вас.

Также важно отметить, что angular может это делать, потому что в нем много метаданных о том, что происходит. Он знает, что это подписка, потому что он анализирует шаблон и находит выражение события (например: (click)="something()") а также из-за декоратора @Output в MyComp

Вы можете довольно четко увидеть логику в классе AppView - LINK Каждое представление наследует от AppView и реализует (среди прочего) функцию с именем destroyInternal. Порядок заключается в том, что при уничтожении destroyLocal() вызывается (определено в AppView), destroyLocal будет очищать все подписки и одноразовые элементы, а затем вызывать destroyInternal, destroyInternal затем будет вызывать ngOnDestroy() для экземпляра вашего компонента.

Решение:

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

Несмотря на простоту, у него есть образец, так как нам нужно:

  • Нам нужно получить ссылку на наш дочерний компонент, мы будем использовать ViewChild
  • Нам нужно подписаться на событие в нашем экземпляре дочернего компонента, но мы можем сделать это только после ngAfterViewInit крюк жизненного цикла запущен.
  • Нам нужно отписаться после того, как мы закончим, это можно сделать несколькими способами, лучший способ - завершить EventEmitter - мы сделаем это в компоненте, который определяет EventEmitter, просто вызовом myEvent.complete(), Это будет распоряжаться всем для нас.

Вот функция уничтожения:

ngOnDestroy() {
  this.myEvent.emit('ngOnDestroy hook');
  this.myEvent.complete();
}

А вот и компонент MyApp:

export class MyApp {
  displayComp = true

  @ViewChild('mycomp', {read: MyComp}) myComp: MyComp;
  constructor() {}

  ngAfterViewInit() {
    this.myComp.myEvent.subscribe( () => alert('myApp > receive event via MANUAL Subscription'));
  }

  test(e) {
    alert('myApp > receive event');
  }
}

Вот рабочий плункер - ССЫЛКА

ОБНОВЛЕНИЕ: Я только что заметил ожидающий пиар для этой проблемы в угловом репо, сейчас 12 дней все еще не объединены, требуется время, чтобы объединить вклад сообщества, поэтому будем надеяться, что он попадет в следующий RC.

Этот PR должен решить эту проблему. Это меняет поведение, которое ngOnDestroy() вызывается до отсоединения событий.

https://github.com/angular/angular/pull/9946

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