1
votes

I'm trying to combine dynamic components (which get created at runtime) and the EventEmitter concept to access data of the child component in the parent component in Angular 8.

My plan is to create a feature where a user can add elements (e.g. cards on a dashboard) dynamically and remove them aswell. In this scenario, the created card has a 'delete' button. This delete button is supposed to propagate the information to the parent component, that the child component can be removed from the array which holds the dynamically created components.

I read in this tutorial from the angular documentation, that i need to create a directive. Now I have the sutation (i think), that the directive sits in between the parent and the child components and I don't know how to correctly emmit the Event in order to delete the child component from the mentioned array.


The Directive

@Directive({
  selector: '[appCards]'
})
export class CardDirective {

  constructor(public viewContainerRef: ViewContainerRef) {
  }

  @Output() directiveDelete = new EventEmitter<any>();
}

Parent Component

card-banner.component.ts

@Component({
  selector: 'app-card-banner',
  templateUrl: './card-banner.component.html',
  styleUrls: ['./card-banner.component.scss']
})
export class CardBannerComponent implements OnInit, OnDestroy  {

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  @Input() cards: CardItem[];

  @ViewChild(CardDirective) appCards: CardDirective;

  loadCards() {
    const viewContainerRef = this.appCards.viewContainerRef;
    viewContainerRef.clear();
    for (const card of this.cards) {
      const componentFactory =
        this.componentFactoryResolver.resolveComponentFactory(card.component);
      const componentRef = viewContainerRef.createComponent(componentFactory);
      (componentRef.instance as CardContentComponent).data = card.data;
    }
  }

  addCard() {
    this.cards.push(new CardItem(CardContentComponent, {name: 'Card Dynamisch'}));
    this.loadCards();
  }

  removeLastCard() {
    this.cards.pop();
    this.loadCards();
  }

  onDelete(deleteBool: any) {
    console.log(deleteBool);
    console.log('delete in card-banner');
  }

  ngOnInit() {this.loadCards();
  }

  ngOnDestroy(): void {
  }

}


card-banner.component.html

<button (click)="addCard()" class="btn">Add Card</button>
<button (click)="removeLastCard()" class="btn">Remove Card</button>

<div style="margin: auto;">
  <ng-template appCards (directiveDelete)="onDelete($event)"></ng-template>
</div>

Child Component

card-content.component.ts

@Component({
  selector: 'app-card',
  templateUrl: './card-content.component.html',
  styleUrls: ['./card-content.component.scss']
})
export class CardContentComponent implements CardInterfaceComponent {

  @Input() data: any;
  @Output() delete = new EventEmitter<any>();

  removeCard() {
    this.delete.emit(true);
    console.log('delete card: '  + this.data.name);
  }
}

card-content.component.html

<div >
  <div style="display: inline;">{{data.name}} <button (click)="removeCard()" class="btn">Delete</button></div>
</div>

I also have a Card-Service, a Card-Interface and a Card-Item class, but I don't think they have an influence in this case, so I didn't post them. If they are neccessary, I can add them.


So, my problem is, that the parent component does not receive the delete message from the child comoponent and therefore, the card can't be deleted.

I hope someone can help me to understand, where the information gets lost and how I should use EventEmitter in this case.

Thank you in advance!

1
Do you want to get notified on event delete from CardContentComponent?Buczkowski
Yes, that would be useful to me.EyedPeas

1 Answers

7
votes

Subscribe to component event after dynamically create it:

  loadCards() {
    const viewContainerRef = this.appCards.viewContainerRef;
    viewContainerRef.clear();
    for (const card of this.cards) {
      const componentFactory = this.componentFactoryResolver.resolveComponentFactory(card.component);
      const componentRef = viewContainerRef.createComponent<CardContentComponent>(componentFactory);
      componentRef.instance.data = card.data;
      componentRef.instance.delete.subscribe(() => {
        // handle delete logic
      });
    }
  }