Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emit event from dynamically created child component to parent component

I have a slider in which are dynamically created items - these are child components.

Parent template where is ng-container for slider:

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

These child components are created by appSliderForm directive:

@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;
  }

}

In my child component, I have a remove function for removing itself from a slider.

@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}`};
  }

}

When this remove function is called by clicking on remove icon in child component template then I would like to emit the event to give a know to parent component to make other operations according to, but the function removeQuestion in parent component is not called.

Could you advise me please why is not called this removeQuestion function?

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

UPDATE

I've debugged it in the chrome browser and I saw that my onRemove EventEmitter object hadn't any values in his the observers array property when the emit function was called on the onRemove object.

this.onRemove.emit(this.questionTest);

Debug

like image 335
rusna Avatar asked Mar 19 '19 22:03

rusna


2 Answers

The problem is that the FormSliderDirective does not have an onRemove event. For your code to work you need to add the event to the directive and subscribe it to the event of the internal component. So whenever the internal event fires it will be propagated to the outside.

Here is a sample how you can add this to your directive:

@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;
  }

}
like image 109
AlesD Avatar answered Sep 19 '22 04:09

AlesD


Perhaps it will help you when the same error happens after applying the solution from @AlesD:

ERROR TypeError: Cannot read property 'subscribe' of undefined

The workaround works for me:

component.instance.onRemove = this.onRemove;
like image 35
boisamazing Avatar answered Sep 20 '22 04:09

boisamazing