EventEmitter的正确用法是什么?


224

我已经读过诸如CustomHttp内的Access EventEmitter Service之类的问题 ,其中用户在其服务中使用EventEmitter,但在此注释中建议他 不要使用它,而应在其服务中直接使用Observables。

我还读了这个 问题 ,解决方案建议将EventEmitter传递给孩子并订阅它。

然后我的问题是:我应该还是不应该手动订阅EventEmitter?我应该如何使用它?



2
和往常一样,马克的回答很好,但实际上他没有解释我解释的原因。我不反对关闭它,但我首先要他的意见。@MarkRajcok有什么想法?
埃里克·马丁内斯

我想保持开放状态(我敢肯定我会把人指在这里-我只是编辑了其他答案以指出这里!)。您的答案有很多其他信息。我想要两个问题标题...另一个是“ EventEmitter的正确用法是什么?”
Mark Rajcok '16

@MarkRajcok我喜欢该标题,但它与当前答案不符,因此我将确保稍后对其进行更新,并添加有关如何使用以及如何不使用它的示例,这样才更有意义。感谢您的反馈:)
Eric Martinez

@MarkRajcok已根据建议(y)编辑(复制并粘贴了建议的标题,所有版权归您所有)。
埃里克·马丁内斯

Answers:


342

TL; DR

不,请勿手动订阅它们,也不要在服务中使用它们。如文档所示,仅使用它们在组件中发出事件。不要打败angular的抽象。

回答:

不,您不应该手动订阅它。

EventEmitter是angular2抽象,其唯一目的是在组件中发出事件。引用Rob Wormald 的评论

[...] EventEmitter实际上是一个Angular抽象,应仅用于在组件中发出自定义事件。否则,就像其他任何库一样使用Rx。

在EventEmitter的文档中确实很清楚地说明了这一点。

由指令和组件使用以发出自定义事件。

使用它有什么问题?

Angular2永远不会保证我们EventEmitter将继续是可观察的。因此,这意味着在代码更改时对其进行重构。我们必须访问的唯一API是emit()方法。我们绝不应该手动订阅EventEmitter。

在沃德·贝尔(Ward Bell)的评论中(建议阅读本文以及对该评论的回答),上述所有内容都更加清楚。报价以供参考

不要指望EventEmitter继续是可观察的!

不要指望将来会出现那些可观察的运算符!

这些将很快被弃用,并且可能在发布之前将其删除。

仅将EventEmitter用于子组件和父组件之间的事件绑定。不订阅。不要调用任何这些方法。只通话eve.emit()

他的评论与很久以前罗布的评论一致。

那么,如何正确使用它呢?

只需使用它即可从您的组件中发出事件。看下面的例子。

@Component({
    selector : 'child',
    template : `
        <button (click)="sendNotification()">Notify my parent!</button>
    `
})
class Child {
    @Output() notifyParent: EventEmitter<any> = new EventEmitter();
    sendNotification() {
        this.notifyParent.emit('Some value to send to the parent');
    }
}

@Component({
    selector : 'parent',
    template : `
        <child (notifyParent)="getNotification($event)"></child>
    `
})
class Parent {
    getNotification(evt) {
        // Do something with the notification (evt) sent by the child!
    }
}

怎么不使用呢?

class MyService {
    @Output() myServiceEvent : EventEmitter<any> = new EventEmitter();
}

停在那...你已经错了...

希望这两个简单的示例可以阐明EventEmitter的正确用法。


1
您的意思是:directives : [Child]在组件定义中?这似乎无法编译,我无法在Angular2文档中找到它。
themathmagician,2016年

1
@Eric:很明显在您的示例中如何不使用它,为什么我们在服务中需要'@Output'装饰器?
trungk17年

1
@themathmagician经过一番研究,我在这里发现该directives关键字已被弃用。按照此处此处的指示使用declarations关键字@NgModule
cjsimon

5
对Toby的最新答案有何评论?我想他的答案应该是当今公认的。
Arjan '18

7
@Eric当您编写此答案时,您写道:“这些将很快被弃用,并且有可能在发布前被删除”,并引用Ward Bell的话。但这是2年前说的,现在我们有了angular6。这个说法现在仍然适用吗?我一直在官方文档中看到EventEmitter仍然具有subscription()方法,因此我认为,如果Google想要停止将EE基于Rxjs主题,他们将已经做到了。那么,您认为您的原始答案仍然很适合Angular的当前状态吗?
Nad G '18年

100

是的,继续使用它。

EventEmitter是最终Angular Core API中公开的,已记录的类型。它是否基于Observable无关紧要;如果其文档emitsubscribe方法适合您的需求,请继续使用它。

正如文档中所述:

使用Rx.Observable,但提供了一个适配器以使其按此处指定的方式工作:https : //github.com/jhusain/observable-spec

一旦该规范的参考实现可用,请切换到该规范。

因此,他们想要一个Observable行为类似的对象,将其实现并公开。如果只是不应该使用的内部Angular抽象,那么他们就不会将其公开。

很多时候,使用一个发送器来发送特定类型的事件很有用。如果那是您的用例,那就去吧。如果/当它们链接到的规格的参考实现可用时,就应该像其他任何polyfill一样直接替换。

只要确保传递给subscribe()函数的生成器遵循链接的规范即可。保证返回的对象具有一个unsubscribe应调用的方法,以释放对生成器的任何引用(当前是RxJs Subscription对象,但这实际上是不应依赖的实现细节)。

export class MyServiceEvent {
    message: string;
    eventId: number;
}

export class MyService {
    public onChange: EventEmitter<MyServiceEvent> = new EventEmitter<MyServiceEvent>();

    public doSomething(message: string) {
        // do something, then...
        this.onChange.emit({message: message, eventId: 42});
    }
}

export class MyConsumer {
    private _serviceSubscription;

    constructor(private service: MyService) {
        this._serviceSubscription = this.service.onChange.subscribe({
            next: (event: MyServiceEvent) => {
                console.log(`Received message #${event.eventId}: ${event.message}`);
            }
        })
    }

    public consume() {
        // do some stuff, then later...

        this.cleanup();
    }

    private cleanup() {
        this._serviceSubscription.unsubscribe();
    }
}

措辞严峻的所有厄运和阴暗预测似乎都来自于一位开发人员在Angular 2的预发行版上发表的Stack Overflow评论。


2
这听起来很合理-还有其他人想对此进行权衡吗?毕竟,subscribe是事件发射器上的公共方法?
肖森

好的,它是公开的,因此我们可以自由使用它。但是在此示例中,是否有实际原因在Event上使用EventEmitter?
博蒂斯

6
我们现在使用的是Angular v6,并且EventEmmiter并未被弃用或删除,因此我认为它可以安全使用。但是,我确实看到了学习如何使用RxJS中的Observables的好处。
大卫·梅萨

我将其视为“如果您在@Output之外使用EventEmitter,则由于api是稳定的ATM,因此无需着急将其更改为其他内容”;如果您有时间或api中断,则必须对其进行更改(但请记住,为了更改角度而更改公共稳定api(发送和订阅)并不常见)。也要坚持使用公共api,不要访问EventEmitter中未定义的Subject或Observable方法,以确保安全起见
acidghost

1
由于“未从Angular中移除”或“有据可查”而可以安全使用某事物的假设以及上述提议的Nono策略都是错误的。您创建的项目不是出于将来的angular版本的原因。外面还有很多未维护的平台仍可以在网络上运行。框架的版本不会使在那些平台上工作的开发人员减少为B类开发人员或开发人员。
克劳迪奥·费拉罗

4

当您想进行跨组件交互时,您需要知道什么是@Input,@Output,EventEmitter和Subjects。

如果组件之间的关系是父子关系,反之亦然,我们将@input和@output与事件发射器一起使用。

@output会发出一个事件,您需要使用事件发射器来发出。

如果不是亲子关系,那么您必须使用主题或通过公共服务。


0

没有:nono,没有:是的。真相在中间,没有理由因为下一个版本的Angular而被吓到。

从逻辑的角度来看,如果您有一个组件并且想要通知其他组件某些事情发生了,那么应该触发一个事件,并且可以用您(开发人员)认为应该完成的任何方式来完成。我不明白为什么不使用它的原因,也不知道不惜一切代价使用它的原因。同样EventEmitter的名称也向我暗示了正在发生的事件。我通常将其用于组件中发生的重要事件。我创建了服务,但是在组件文件夹中创建了服务文件。因此,我的服务文件变成了一种事件管理器或事件接口,因此我一眼就能确定可以在当前组件上订阅哪个事件。

我知道。也许我是一个老式的开发人员。但这不是事件驱动开发模式的一部分,这是您特定项目的软件体系结构决策的一部分。

其他一些人可能认为直接使用Observables很酷。在这种情况下,请直接使用Observables。您不是这样做的连环杀手。除非您是一个精神病患者开发人员,否则到目前为止,该程序仍然有效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.