typescript 子组件事件广播给父组件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34802210/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:12:57  来源:igfitidea点击:

Child component events broadcast to parent

typescriptangularangular2-directives

提问by Hyman Guy

I'd like to implement the common Angular 1.x pattern of having child directives within a parent directive in Angular 2. Here's my desired structure.

我想实现常见的 Angular 1.x 模式,即在 Angular 2 的父指令中包含子指令。这是我想要的结构。

<foo>
  <bar>A</bar>
  <bar>B</bar>
  <bar>C</bar>
</foo>

I'd like for these Barcomponents to have clickevents that get emitted to the Foocomponent.

我希望这些Bar组件具有click发送到Foo组件的事件。

Here's my Fooso far:

这是我Foo到目前为止:

@Component({
  selector: 'foo',
  template: `
    <div>
      <ng-content></ng-content>
    </div>
  `
})
export class Foo {
   @ContentChildren(Bar) items: QueryList<Bar>;
}

And here's my Bar:

这是我的Bar

@Component({
  selector: 'Bar',
  template: `
    <div (click)="clickity()">
      <ng-content></ng-content>
    </div>
  `
})
export class Bar {
  clickity() {
    console.log('Broadcast this to the parent please!');
  }
}

How do I go about notifying Foowhenever one of its Barsis clicked?

Foo每当Bars单击其中一个时,我如何进行通知?

采纳答案by Mike Lovelace

The other answer does a very poor job of solving the problem. EventEmitters are only meant to be used in conjunction with @Outputsas well as this problem not taking advantage of the dependency injection built into Angular 2 or the features of RxJS.

另一个答案在解决问题方面做得很差。EventEmitters 只是为了@Outputs解决这个问题,而不是利用 Angular 2 内置的依赖注入或 RxJS 的特性。

Specifically, by not using DI, you're forcing yourself into a scenario where if you reuse the components dependent on the static class they will all receive the same events, which you probably don't want.

具体来说,通过不使用 DI,您会强迫自己进入一个场景,如果您重用依赖于静态类的组件,它们都将收到相同的事件,而您可能不希望这样。

Please take a look at the example below, by taking advantage of DI, it is easy to provide the same class multiple times, enabling more flexible use, as well as avoiding the need for funny naming schemes. If you want multiple events, you could provide multiple versions of this simple class using opaque tokens.

请看下面的例子,利用DI,可以很容易地多次提供同一个类,实现更灵活的使用,同时也避免了有趣的命名方案的需要。如果您想要多个事件,您可以使用不透明标记提供这个简单类的多个版本。

Working Example: http://plnkr.co/edit/RBfa1GKeUdHtmzjFRBLm?p=preview

工作示例:http: //plnkr.co/edit/RBfa1GKeUdHtmzjFRBLm?p=preview

// The service
import 'rxjs/Rx';
import {Subject,Subscription} from 'rxjs/Rx';

export class EmitterService {
  private events = new Subject();
  subscribe (next,error,complete): Subscriber {
    return this.events.subscribe(next,error,complete);
  }
  next (event) {
    this.events.next(event);
  }
}

@Component({
  selector: 'bar',
  template: `
    <button (click)="clickity()">click me</button>
  `
})
export class Bar {
  constructor(private emitter: EmitterService) {}
  clickity() {
    this.emitter.next('Broadcast this to the parent please!');
  }
}

@Component({
  selector: 'foo',
  template: `
    <div [ngStyle]="styl">
      <ng-content></ng-content>
    </div>
  `,
  providers: [EmitterService],
  directives: [Bar]
})
export class Foo {
  styl = {};
  private subscription;
  constructor(private emitter: EmitterService) {
    this.subscription = this.emitter.subscribe(msg => {
      this.styl = (this.styl.background == 'green') ? {'background': 'orange'} : {'background': 'green'};
    });
  }
  // Makes sure we don't have a memory leak by destroying the
  // Subscription when our component is destroyed
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

回答by Sasxa

You can use a service to send data between components if you can't do it using @Output()decorator. Here's an example:

如果您不能使用@Output()装饰器来发送数据,您可以使用服务在组件之间发送数据。下面是一个例子:

import {EventEmitter} from 'angular2/core';

export class EmitterService {
  private static _emitters: { [channel: string]: EventEmitter<any> } = {};
  static get(channel: string): EventEmitter<any> {
    if (!this._emitters[channel]) 
      this._emitters[channel] = new EventEmitter();
    return this._emitters[channel];
  }
}

You import it wherever you need to emit or subscribe to an event:

您可以在需要发出或订阅事件的任何地方导入它:

// foo.component.ts
import {EmitterService} from '../path/to/emitter.service'

class Foo {
  EmitterService.get("some_id").subscribe(data => console.log("some_id channel: ", data));
  EmitterService.get("other_id").subscribe(data => console.log("other_id channel: ", data));
}

// bar.component.ts
import {EmitterService} from '../path/to/emitter.service'

class Bar {

  onClick() {
    EmitterService.get("some_id").emit('you clicked!');
  }
  onScroll() {
    EmitterService.get("other_id").emit('you scrolled!');
  }
}

another example: plunker

另一个例子:plunker

回答by Mohsen Tabareh

Why not using @ContentChildern?

为什么不使用@ContentChildern?

in bar.component.ts we expose clicked event

在 bar.component.ts 中,我们公开了 clicked 事件

@Output() clicked = new EventEmitter<BarComponent>();
onClick(){
    this.clicked.emit(this);
}

in foo.component.ts we subscribe to the clicked event of each

在 foo.component.ts 中我们订阅了每个的 clicked 事件

 @ContentChildren(BarComponent) accordionComponents: QueryList<BarComponent>;

 ngAfterViewInit() {
 this.accordionComponents.forEach((barComponent: BarComponent) => {
        barComponent.clicked.subscribe((bar: BarComponent) => doActionsOnBar(bar));           
    });
}