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

#angular #angular5 #angular-directive #angular-dynamic-components #angular-event-emitter

#angular #angular5 #angular-директива #angular-dynamic-components #angular-событие-эмиттер

Вопрос:

У меня есть слайдер, в котором динамически создаются элементы — это дочерние компоненты.

Родительский шаблон, где находится ng-контейнер для слайдера:

 <div id="slider-wrapper">
    <ng-container appSliderForm *ngFor="let question of questionsInSlider"
                          [questionTest]="question" (onRemove)="removeQuestion($event)">
    </ng-container>
</div>
  

Эти дочерние компоненты создаются директивой appSliderForm:

 @Directive({
  selector: '[appSliderForm]'
})
export class FormSliderDirective implements OnInit {

  @Input()
  questionTest: QuestionInSlider;

  constructor(private resolver: ComponentFactoryResolver, private container: ViewContainerRef) {}

  ngOnInit(): void {
    const factory = this.resolver.resolveComponentFactory<TestQuestionInSliderComponent>(TestQuestionInSliderComponent);
    const component = this.container.createComponent(factory);
    component.instance.questionTest = this.questionTest;
    component.instance.ref = component;
  }

}
  

В моем дочернем компоненте у меня есть функция remove для удаления самого себя из слайдера.

 @Component({
  selector: 'app-test-question-in-slider',
  templateUrl: './test-question-in-slider.component.html',
  styleUrls: ['./test-question-in-slider.component.less']
})
export class TestQuestionInSliderComponent {

  questionTest: QuestionInSlider;

  ref: any;

  @Output() public onRemove = new EventEmitter<QuestionInSlider>();

  constructor(private builderService: FormBuilderService) {}

  /**
   * Chosen question from slider will be displayed.
   */
  choose(): void {
    this.questionTest.chosen = true;
    this.builderService.handlerQuestionFromSlider(this.questionTest);
  }

  remove(): void {
    this.onRemove.emit(this.questionTest);
    this.ref.destroy();
  }

  isChosen() {
    return {'chosen': this.questionTest.chosen};
  }

  getBorderTopStyle() {
     return {'border-top': `4px solid ${this.questionTest.color}`};
  }

}
  

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

Не могли бы вы посоветовать мне, пожалуйста, почему не вызывается эта функция removeQuestion?

 removeQuestion(question: QuestionInSlider) {
    console.log(question);
  }
  

Обновить

Я отладил его в браузере Chrome и увидел, что у моего объекта EventEmitter onRemove не было никаких значений в его свойстве массива наблюдателей, когда для объекта onRemove была вызвана функция emit.

 this.onRemove.emit(this.questionTest);
  

Отладка

Ответ №1:

Проблема в том, что у FormSliderDirective нет onRemove события. Для того, чтобы ваш код работал, вам необходимо добавить событие в директиву и подписаться на него на событие внутреннего компонента. Таким образом, всякий раз, когда срабатывает внутреннее событие, оно будет распространяться наружу.

Вот пример того, как вы можете добавить это в свою директиву:

 @Directive({
  selector: '[appSliderForm]'
})
export class FormSliderDirective implements OnInit {

  @Input() questionTest: QuestionInSlider;
  @Output() public onRemove = new EventEmitter<QuestionInSlider>();

  constructor(private resolver: ComponentFactoryResolver, private container: ViewContainerRef) {}

  ngOnInit(): void {
    const factory = this.resolver.resolveComponentFactory<TestQuestionInSliderComponent>(TestQuestionInSliderComponent);
    const component = this.container.createComponent(factory);
    component.instance.questionTest = this.questionTest;
    component.instance.onRemove.subscribe(this.onRemove); // this connects the component event to the directive event
    component.instance.ref = component;
  }

}
  

Комментарии:

1. Прошу прощения за поздний ответ. Что касается вашего решения, у меня это не работает. Я отладил его и обнаружил, что мой инициализированный объект EventEmitter onRemove в моем TestQuestionInSliderComponent имел свойство observers для пустого массива. Оно не должно содержать никаких значений?

2. Хорошо, спасибо, что подтолкнули меня. Благодаря вам я нашел решение. Это было просто необходимо для изменения вашего решения.

3. Извините за мой поздний ответ. Рад, что вы это поняли. Я допустил ошибку, и, как вы обнаружили, вам нужно переключить подписки. Я обновил свой ответ. Вы можете выполнить прямую подписку, поскольку отправители событий являются субъектами.

Ответ №2:

Возможно, это поможет вам, когда такая же ошибка возникает после применения решения от @AlesD:

 ERROR TypeError: Cannot read property 'subscribe' of undefined
  

Обходной путь работает для меня:

 component.instance.onRemove = this.onRemove;