Reputation: 6946
I have an element in HTML template of an Angular 2 app. I add a directive to it:
<div myCustomDirective>HELLO</div>
I want that whenever I hover over the div
the text inside the div
should be changed, but it needs to be done from Directive
(mouseover)
event.
How to emit an event from a Directive
and capture it inside a parent element?
Upvotes: 53
Views: 69464
Reputation: 2974
You can also use the use the same name for the directive and the @Output
:
@Directive({
selector: '[myCustomMouseover]'
})
export class MyCustomMouseoverDirective {
@Output()
public myCustomMouseover = new EventEmitter<void>();
@HostListener('mouseover', ['$event'])
public onMouseover(event: MouseEvent): void {
if (/* only trigger in certain conditions */) {
this.myCustomMouseover.emit();
}
}
}
And you can use in any element just like:
<div (myCustomMouseover)="handler()">
...
</div>
Upvotes: 10
Reputation: 1340
This is my solution with Angular 13. I plan to create a pagination component so ignore the name.
import {Directive, EventEmitter, Input, OnInit, Output, TemplateRef, ViewContainerRef} from '@angular/core';
@Directive({
selector: '[appPaginate]'
})
export class PaginateDirective implements OnInit {
@Output() newItemEvent: EventEmitter<string> = new EventEmitter<string>()
constructor( private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
ngOnInit() {
}
@Input() set appPaginate(condition: any) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.newItemEvent.emit('Directive working')
}
}
}
<ng-template [appPaginate]="condition" (newItemEvent)="update($event)">
<p>{{valueFromDirective}}</p>
</ng-template>
import {Component, Input, OnInit} from '@angular/core';
import {Item} from "./item";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title = 'tutorial';
condition = true;
valueFromDirective = this.title;
ngOnInit() {
}
update($event: any) {
this.valueFromDirective = $event;
}
}
Building on what @Alexander and @Zochbauer's discussion. With <ng-template>
, you can define template content that is only being rendered by Angular when you, whether directly or indirectly, specifically instruct it to do so, allowing you to have full control over how and when the content is displayed. Thus when your condition is met you will be required to use this line to display the emitted value onto the html:
this.viewContainer.createEmbeddedView(this.templateRef);
N.B. This is only to help those who think event emitter doesn't work on Angular 7+.
Upvotes: 0
Reputation: 14579
I'd like to add to @GünterZöchbauer's answer that if you're trying to emit an event from a structural directive and using an asterisk (*
) syntax when applying the directive, it won't work. Angular 5.2.6 still doesn't support @Output
binding for structural directives if used with the *
syntax (see GitHub issue).
You have to transform it to de-sugarized form (see here), i.e.:
<ng-template [customDirective]="foo" (customDirectiveEvent)="handler($event)">
<div class="name">{{hero.name}}</div>
</ng-template>
instead of:
<div *customDirective="foo" (customDirectiveEvent)="handler($event)" class="name">{{hero.name}}</div>
Upvotes: 35
Reputation: 658067
If myCustomDirective
has an output @Output() someEvent:EventEmitter = new EventEmitter();
then you can use
<div myCustomDirective (someEvent)="callSomethingOnParent($event)">HELLO</div>
Upvotes: 105