6

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

rusna
  • 366
  • 6
  • 20

2 Answers2

5

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

}
Aleš Doganoc
  • 11,568
  • 24
  • 40
  • I'm sorry for the late response. To your solution, this is not working for me. I've debugged it and I found that my initialized **onRemove** EventEmitter object in my TestQuestionInSliderComponent had the property **observers** the empty array. It shouldn't contain any values? – rusna Mar 23 '19 at 18:38
  • Okay, thank you for to shoving me. I found the solution thanks to you. It was just needed to turn around your solution. – rusna Mar 24 '19 at 16:03
  • 1
    Sorry for my late reply. Glad you figured it out. I made a mistake and as you discovered you need to switch the subscriptions. I have updated my answer. You can do direct subscribe since EventEmitters are subjects. – Aleš Doganoc Mar 25 '19 at 08:23
1

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;
boisamazing
  • 230
  • 3
  • 7