我在CustomHttp中读过类似访问EventEmitter Service的问题 用户在他的服务中使用EventEmitter,但他在这个评论中被建议 不使用它,而是直接在他的服务中使用Observables。

我也读过这个 问题 解决方案建议将EventEmitter传递给子进程并订阅它。

我的问题是:我是否应该手动订阅EventEmitter?我该如何使用它?


当前回答

是的,尽管用吧。

EventEmitter是一个在最终的Angular核心API中文档化的公共类型。它是否基于可观察对象无关紧要;如果它记录的emit和subscribe方法适合您的需要,那么就使用它。

正如文件中所述:

使用的处方。可观察对象,但提供了一个适配器,使其按此处指定的方式工作:https://github.com/jhusain/observable-spec 一旦该规范的参考实现可用,就切换到它。

所以他们想要一个类似于Observable的对象,以某种方式表现,他们实现了它,并将其公开。如果它只是一个不应该被使用的Angular内部抽象,他们就不会公开它。

在很多情况下,使用发送特定类型事件的发射器是很有用的。如果这是你的用例,那就去做吧。如果/当他们链接到的规范的参考实现可用时,它应该是一个附带的替代品,就像任何其他填充材料一样。

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

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评论。

其他回答

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

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

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

如果不是亲子关系…然后你必须使用主题或通过公共服务

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

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

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

如果不是亲子关系…然后你必须使用主题或通过公共服务。

是的,尽管用吧。

EventEmitter是一个在最终的Angular核心API中文档化的公共类型。它是否基于可观察对象无关紧要;如果它记录的emit和subscribe方法适合您的需要,那么就使用它。

正如文件中所述:

使用的处方。可观察对象,但提供了一个适配器,使其按此处指定的方式工作:https://github.com/jhusain/observable-spec 一旦该规范的参考实现可用,就切换到它。

所以他们想要一个类似于Observable的对象,以某种方式表现,他们实现了它,并将其公开。如果它只是一个不应该被使用的Angular内部抽象,他们就不会公开它。

在很多情况下,使用发送特定类型事件的发射器是很有用的。如果这是你的用例,那就去做吧。如果/当他们链接到的规范的参考实现可用时,它应该是一个附带的替代品,就像任何其他填充材料一样。

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

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评论。

TL; diana:

不要手动订阅它们,不要在服务中使用它们。按照文档中所示的方式使用它们,只用于在组件中触发事件。不要破坏angular的抽象。

答:

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

EventEmitter是一个angar2抽象,它的唯一目的是在组件中发射事件。引用Rob Wormald的评论

[…EventEmitter实际上是Angular的一个抽象,应该只用于在组件中发射自定义事件。否则,就像使用其他库一样使用Rx。

这在EventEmitter的文档中说得很清楚。

使用by指令和组件来发出自定义事件。

使用它有什么问题?

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的正确用法。

从纯实现的角度来看,因为emit和subscribe是EventEmitter公共接口的一部分,所以可以将它们用于实现。

对于angular来说,如果它不想要的话,没有强制从行为中继承,行为可以是EventEmitter类中的私有成员,比如,

public class EventEmitter{
  private _behaviour=new Subject<void>();
  private _behaviour$=this._behaviour.asObservable();
  ......
  public emit(){
    _behaviour.emit();
  }
  ....
}

如果它继承了行为,但行为不像行为,则违反了利斯科夫替换原理。