Angular 2:如何通过多个嵌套组件发出事件?

Tys*_*son 15 angular

我有一个名为ItemsComponent的 "智能"组件和两个嵌套的"哑"组件,ItemsListComponentItemComponent.

ItemsComponent的html模板包含ItemsListComponent.

// ItemsComponent
<div>
  // ItemsListComponent
  <app-items-list
    [items]="items"
    (doDelete)="deleteItem($event)"
  >
  </app-items-list>
<div>
Run Code Online (Sandbox Code Playgroud)

它有一个名为deleteItem的函数:

deletItem(item) {
  // code to handle item deletion...
}
Run Code Online (Sandbox Code Playgroud)

ItemsListComponent包含ItemComponent:

// ItemsListComponent    
<ul *ngFor="let item of items">
  // ItemComponent
  <app-item
   [item]="item"
   (doDelete)="deleteItem($event)"
  >
  </app-item>
</ul>
Run Code Online (Sandbox Code Playgroud)

所以html结构是这样的:

ItemsComponent (app-items)
    - ItemsListComponent (app-items-list)
        - ItemComponent (app-item)
Run Code Online (Sandbox Code Playgroud)

ItemComponent有一个按钮

<button (click)="deleteItem(item)">
Run Code Online (Sandbox Code Playgroud)

deleteItem的事件发射器:

@Output() doDelete = new EventEmitter();

deleteItem(item) {
  this.doDelete.emit(item);
}
Run Code Online (Sandbox Code Playgroud)

当在ItemComponent中单击删除按钮时,该事件仅冒泡到它的直接父项ItemsListComponent,但不会使它成为ItemsComponent,除非我向ItemsListComponent添加相同的事件发射器功能.

Smelly ItemsListComponent:

@Output() doDelete = new EventEmitter();

deleteItem(item) {
  this.doDelete.emit(item);
}
Run Code Online (Sandbox Code Playgroud)

它以这种方式工作,但ItemsListComponent现在与ItemsComponent共享代码气味,因为它们都具有相同的事件发射器内容,并且事件必须在一个组件上传递到另一个组件.

有一个更好的方法吗?

sno*_*ete 21

正如您所知,自定义角度事件不会冒泡,因此如果您有一个深层嵌套的组件,并且您希望将事件传递给更高的组件,则两者之间的每个组件都必须向上委派事件.

另一种选择是将您的deleteItem功能转移到注入其中一个较低级别组件的服务.这样,可以在它发生的位置调用该函数,而不必将事件冒泡到视图层次结构中.


Jos*_*man 13

CustomEvent实际上,您可以使用 a而不是 来实现它EventEmitter。这只是一种解决方法,特别是因为Angular 文档和许多其他地方都很好地描述了 Angular 通信模式。

基本上,您需要从内部子组件触发 aCustomEvent并简单地在最外层组件(祖父母)上监听它。

最外层组件

@Component({
 selector: 'outermost',
 template: `<child></child>`
})
export class OutermostComponent {
      @HostListener('FormSubmitCustomEvent', ['$event'])
      onCustomEventCaptured(event: any) {
        console.log('Event Received', event.detail);
      }
}
Run Code Online (Sandbox Code Playgroud)

子组件

@Component({
  selector: 'child',
  template: `<inner-child></inner-child>`
})
export class ChildComponent {
   
}
Run Code Online (Sandbox Code Playgroud)

内部子组件

@Component({
  selector: 'inner-child',
  template: `<button (click)="onSubmit()"></button>`
})
export class InnerChildComponent {

constructor(private elementRef: ElementRef) {}

onSubmit(): void {
    const event: CustomEvent = new CustomEvent('FormSubmitCustomEvent', {
      bubbles: true,
      detail: { data: 'Hello from Inner Child' }
    });

    this.elementRef.nativeElement.dispatchEvent(event);
  }
}
Run Code Online (Sandbox Code Playgroud)

这是一个有效的闪电战:https ://stackblitz.com/edit/angular-5wv8p5