将事件从指令发送到父元素

Emit event from Directive to parent element

我在 Angular 2 应用程序的 HTML 模板中有一个元素。我给它添加了一个指令:

<div myCustomDirective>HELLO</div>

我希望每当我将鼠标悬停在 div 上时,div 中的文本都应该更改,但这需要在 Directive (mouseover) 事件中完成。

如何从 Directive 发出事件并将其捕获到父元素中?

如果myCustomDirective有一个输出@Output() someEvent:EventEmitter = new EventEmitter();那么你可以使用

<div myCustomDirective (someEvent)="callSomethingOnParent($event)">HELLO</div>

我想在@GünterZöchbauer 的回答中补充一点,如果您尝试从 structural 指令发出事件并使用星号 (*)应用指令时的语法,它将不起作用。 Angular 5.2.6 仍然不支持 @Output 绑定结构指令如果与 * 语法一起使用(参见 GitHub issue)。

您必须将其转换为 de-sugarized 形式 (see here),即:

<ng-template [customDirective]="foo" (customDirectiveEvent)="handler($event)">
  <div class="name">{{hero.name}}</div>
</ng-template>

而不是:

<div *customDirective="foo" (customDirectiveEvent)="handler($event)" class="name">{{hero.name}}</div>

这是我使用 Angular13 的解决方案。我打算创建一个分页组件,因此请忽略名称。

指令:

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')
    }
  }
}

Component.html:

<ng-template [appPaginate]="condition" (newItemEvent)="update($event)">
  <p>{{valueFromDirective}}</p>
</ng-template>

Component.ts

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;

  }
}

解释

基于@Alexander 和@Zochbauer 的讨论。使用 <ng-template>,您可以定义只由 Angular 渲染的模板内容,当您直接或间接地特别指示它这样做时,允许您完全控制内容的方式和时间被展示。因此,当满足您的条件时,您将需要使用此行将发出的值显示到 html:

this.viewContainer.createEmbeddedView(this.templateRef);

N.B。这只是为了帮助那些认为事件发射器在 Angular 7+.

上不起作用的人

您也可以使用与指令相同的名称 @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();
    }
  }
}

您可以在任何元素中使用,例如:

<div (myCustomMouseover)="handler()">
  ...
</div>