Reputation: 970
I have a component with an event on it like this:
<menu-elem (onClick)="reallyCoolEvent($event)"></custom-component>
Right now, i can trigger my reallyCoolEvent
with an EventEmitter from my custom-component
.
@Output() onClick= new EventEmitter();
this.onClick.emit(stuff);
Like this, in my other component the function reallyCoolEvent
will be called and the stuff
will be passed to that function. That works really well!
Now, lets say, I want to copy that component for whatever reason and I want to trigger the same event from the original. Right now, I'm going through my list of onClick
and if they macth my criteria I store them in a list that I show somewhere else on the page. When I trigger the onClick
from there the event is never triggered.
<menu>
<menu-elem label="Action 1" (onClick)="superCoolEvent($event)"></menu-elem>
<menu-elem label="Action 2" (onClick)="superCoolEvent2($event)"></menu-elem>
<menu-elem label="Action 3" (onClick)="superCoolEvent3($event)"></menu-elem>
</menu>
In menu
:
template:
<div>
<ng-content></ng-content>
</div>
<sub-menu *ngFor="let subMenu of subMenus" [style.top]="subMenu.top" [style.left]="subMenu.left" [items]="subMenu.items"></sub-menu>
In sub-menu
:
template:
<menu-elem *ngFor="let item of items" [label]="item.label"></menu-elem>
By doing that, I would like to be able to trigger the onClick
within the <div class="here">
and still trigger the event from the original template : reallyCoolEvent($event)
. I don't know if I can do that or not. Of course the onClick
is not set because I don't have the data anymore from the template. Or at least, I don't know how to get the data.
The only solution that I have so far, is to duplicate my entries like this:
<menu>
<menu-elem label="1" (onClick)="superCoolEvent($event)"></menu-elem>
<menu-elem label="2" (onClick)="superCoolEvent2($event)"></menu-elem>
<menu-elem label="3" (onClick)="superCoolEvent3($event)"></menu-elem>
<div class="test">
<menu-elem label="1" (onClick)="superCoolEvent($event)"></menu-elem>
<menu-elem label="2" (onClick)="superCoolEvent2($event)"></menu-elem>
<menu-elem label="3" (onClick)="superCoolEvent3($event)"></menu-elem>
</div>
</menu>
And depending of my criteria I can show them or not, but I don't like that way since the template gets too big for nothing.
Upvotes: 2
Views: 2983
Reputation: 14564
Your basic problem is that custom events don't bubble in Angular.
Since your menu
looks like this:
<div>
<ng-content></ng-content>
</div>
<sub-menu *ngFor="let subMenu of subMenus" [items]="subMenu.items"></sub-menu>
Any event triggered within sub-menu
in the template above won't automatically be available outside outside of sub-menu
.
So, if you want the customEvent
from within your sub-menu
template to be available outside of sub-menu
, you have to manually propagate the event through. Something like:
<div>
<ng-content></ng-content>
</div>
<div class="here">
<sub-menu *ngFor="let item of items" label="item.label" (submenuCustomEvent)="menuCustomEvent.emit($event)"></sub-menu>
</div>
where submenuCustomEvent
is an EventEmitter
property of sub-menu
and menuCustomEvent
is an EventEmitter
property of menu
.
EDIT:
That means that your sub-menu
has to look something like:
@Component({
.....
template: `
<menu-elem *ngFor="let item of items"
[label]="item.label"
(customEvent)="submenuCustomEvent.emit($event)
>
</menu-elem>
`
})
export class Submenu {
.....
@Output()
submenuCustomEvent = new EventEmitter();
....
}
and your menu
becomes:
@Component({
.....
template: `
<div>
<ng-content></ng-content>
</div>
<sub-menu *ngFor="let subMenu of subMenus"
[items]="subMenu.items"
(submenuCustomEvent)="menuCustomEvent.emit($event)"
>
</sub-menu>
`
})
export class Menu {
.....
@Output()
menuCustomEvent = new EventEmitter();
....
}
So, the template that you're using your menu
now looks like:
<menu (menuCustomEvent)="superCoolEvent($event)">
<menu-elem label="Action 1" (customEvent)="superCoolEvent($event)"></menu-elem>
<menu-elem label="Action 2" (customEvent)="superCoolEvent2($event)"></menu-elem>
<menu-elem label="Action 3" (customEvent)="superCoolEvent3($event)"></menu-elem>
</menu>
Points to note:
If you need to distinguish which menu-elem
nested within sub-menu
emitted the event, you might have to add some additional information to the event emitted by submenuCustomEvent
.
I named the event emitters for menu
and sub-menu
differently in my example to highlight the difference, but you can use the same event emitter name in your code (i.e. call all the nested event emitters onClick
)
A service might help in this scenario to avoid the manual wiring up of event emitters through the component hierarchy. Using a service, your top level component that's using menu
can be directly notified if a menu-elem
within sub-menu
is clicked, without having the intermediate event propagation
Finally, you are using a custom onClick
event. But if you use the browser click
event directly, you could avoid the manual propagation since browser click
events DO propagate. However, you don't have control over the shape of the emitted event, so there is that limitation.
Upvotes: 1
Reputation: 17879
I am not sure if I understood you fully. However, you can inject a reference of Parent component to child and use parent's eventemiiter.
constructor(private myParent: Parent) {}
go() {
this.myParent.custometEvent.emit(data);
}
Upvotes: 0
Reputation: 41571
Your answer is as below
<custom-component *ngFor="let item of items" label="item.label" (customEvent)="eventHandler($event)"></custom-component>
The corresponding code should be in component-x ts file
eventHandler(event){
this.customEvent.emit(..)
}
This will trigger the customEvent() in your custom-component as in this line
<custom-component (customEvent)="reallyCoolEvent($event)"></custom-component>
So this method
reallyCoolEvent(event){
///here is your emitted object from component-x
}
Upvotes: 0