子组件事件广播给父组件
Child component events broadcast to parent
我想在 Angular 2 中实现在父指令中包含子指令的常见 Angular 1.x 模式。这是我想要的结构。
<foo>
<bar>A</bar>
<bar>B</bar>
<bar>C</bar>
</foo>
我希望这些 Bar
组件有 click
事件发送到 Foo
组件。
这是我目前的Foo
:
@Component({
selector: 'foo',
template: `
<div>
<ng-content></ng-content>
</div>
`
})
export class Foo {
@ContentChildren(Bar) items: QueryList<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!');
}
}
如何在点击其中一个 Bars
时通知 Foo
?
如果无法使用 @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];
}
}
在需要发出或订阅事件的任何地方导入它:
// 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!');
}
}
另一个例子:plunker
另一个答案在解决问题方面做得很差。 EventEmitters 只能与 @Outputs
结合使用,并且这个问题没有利用 Angular 2 中内置的依赖注入或 RxJS 的特性。
具体来说,如果不使用 DI,您就会强迫自己进入这样一种场景:如果您重用依赖于静态 class 的组件,它们都会收到相同的事件,而您可能不希望这样.
请看下面的例子,利用 DI 可以很容易地多次提供相同的 class,使用起来更灵活,同时也避免了有趣的命名方案.如果您想要多个事件,您可以使用不透明标记提供这个简单 class 的多个版本。
工作示例:
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();
}
}
为什么不使用@ContentChildern?
在bar.component.ts中我们公开点击事件
@Output() clicked = new EventEmitter<BarComponent>();
onClick(){
this.clicked.emit(this);
}
在foo.component.ts我们订阅了每个
的点击事件
@ContentChildren(BarComponent) accordionComponents: QueryList<BarComponent>;
ngAfterViewInit() {
this.accordionComponents.forEach((barComponent: BarComponent) => {
barComponent.clicked.subscribe((bar: BarComponent) => doActionsOnBar(bar));
});
}
我想在 Angular 2 中实现在父指令中包含子指令的常见 Angular 1.x 模式。这是我想要的结构。
<foo>
<bar>A</bar>
<bar>B</bar>
<bar>C</bar>
</foo>
我希望这些 Bar
组件有 click
事件发送到 Foo
组件。
这是我目前的Foo
:
@Component({
selector: 'foo',
template: `
<div>
<ng-content></ng-content>
</div>
`
})
export class Foo {
@ContentChildren(Bar) items: QueryList<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!');
}
}
如何在点击其中一个 Bars
时通知 Foo
?
如果无法使用 @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];
}
}
在需要发出或订阅事件的任何地方导入它:
// 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!');
}
}
另一个例子:plunker
另一个答案在解决问题方面做得很差。 EventEmitters 只能与 @Outputs
结合使用,并且这个问题没有利用 Angular 2 中内置的依赖注入或 RxJS 的特性。
具体来说,如果不使用 DI,您就会强迫自己进入这样一种场景:如果您重用依赖于静态 class 的组件,它们都会收到相同的事件,而您可能不希望这样.
请看下面的例子,利用 DI 可以很容易地多次提供相同的 class,使用起来更灵活,同时也避免了有趣的命名方案.如果您想要多个事件,您可以使用不透明标记提供这个简单 class 的多个版本。
工作示例: 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();
}
}
为什么不使用@ContentChildern?
在bar.component.ts中我们公开点击事件
@Output() clicked = new EventEmitter<BarComponent>();
onClick(){
this.clicked.emit(this);
}
在foo.component.ts我们订阅了每个
的点击事件 @ContentChildren(BarComponent) accordionComponents: QueryList<BarComponent>;
ngAfterViewInit() {
this.accordionComponents.forEach((barComponent: BarComponent) => {
barComponent.clicked.subscribe((bar: BarComponent) => doActionsOnBar(bar));
});
}