Capture event on ng-content in Angular 2
Asked Answered
V

3

14

I am going through this tutorial to comprehend angular 2's ng-content. I want to capture event which is triggered on ng-content. I have following component:

@Component({
    moduleId: module.id,
    selector: 'card',
    template: `
    <ng-content (click)="onClick($event)"></ng-content>
    `
})
export class CardComponent {

    onClick(){
        console.log('clicked');
    }
}

Here, as you can see I am setting a listener to the click event. But for some reasons it is not triggering. It seems like the entire ng-content tag is replaced. So to capture the event currently I am doing this:

template: `
    <div (click)="onClick($event)">
      <ng-content></ng-content>
    </div>
    `

Is there any better way to do this? cause I don't want to wrap my ng-content into a div to avoid styling issues. Thanks. Plnkr

Vivienne answered 2/3, 2017 at 11:32 Comment(1)
Did you find a resolution to this?Crossbreed
J
8

You can achieve pretty the same result by binding event listeners to the component element with host declaration.

import { Component, HostListener } from '@angular/core';

@Component({
  selector: 'card',
  template: `<ng-content></ng-content>`
})
export class ButtonComponent {

  @HostListener('click', ['$event.target'])
  onClick(target) {
    console.log('Clicked on: ', target);
  }

}
Jessalyn answered 1/6, 2018 at 13:32 Comment(1)
Accessibility is a problem for this approach. The component <card> does not track keyboard users. You'd need a <button card> directive which may or not work on your use case. Buttons and anchors (click) does properly track keyboard enter or screen reader's "click".Nicknack
S
5

You can use @ContentChild to capture the events of projected component.

Child:

@Component({
moduleId: module.id,
selector: 'card-child',
template: `
<div>Card Child component</div>
`
})
export class CardChildComponent {
     @Output customEvent:EventEmitter = new EventEmitter()
}

Parent:

@Component({
moduleId: module.id,
selector: 'card',
template: `
<ng-content></ng-content>
`
})
export class CardComponent {
    @ContentChild(CardChildComponent) childComp: CardChildComponent;

    ngAfterContentInit() {
        this.childComp.addEventListener("click", ()=>{console.log("clicked"});
        //Below is handling of custom event
        this.childComp.customEvent.subscribe(()=>{console.log("clicked"});
    }
}

Below is how the component will be injected.

<card>
<card-child></card-child>
</card>
Salable answered 23/10, 2020 at 13:8 Comment(1)
not working if you have multiple card-child components inside card component. works only for first oneOryx
A
0

<ng-content> is just a placeholder to transclude the contents inside a component's tags. Angular replaces it.

The only option to avoid using the DIV solution, would be to create your own version of ng-content.

Basically, you just create an empty component and, in the template, you only put a <ng-content> tag. If you call that component AppContentComponent (app-content), then you can, in your components,

<app-content (click)="onClick()">
   <ng-content></ng-content>
</app-content>

Of course, this is a bit convoluted, and you end up wrapping your transcluded content into another tag (not a <div>, but an <app-content>), though, of course, initially you didn't mind the content being wrapped by an <ng-content> so I suppose it might work for you.

Aidoneus answered 15/3, 2018 at 9:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.