15

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

1
  • Did you find a resolution to this? Commented Oct 30, 2017 at 18:59

3 Answers 3

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); } } 
Sign up to request clarification or add additional context in comments.

1 Comment

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".
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> 

1 Comment

not working if you have multiple card-child components inside card component. works only for first one
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.