Angular中的全局事件

Angular中没有等价的$scope.emit()$scope.broadcast()吗?

我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。

如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?

150752 次浏览

你可以使用EventEmitter或可观察对象来创建一个注册到DI的事件总线服务。每个想要参与的组件只需将服务作为构造函数参数请求,并发出和/或订阅事件。

另请参阅

AngularJS中没有等价的$scope.emit()$scope.broadcast()。 组件内部的EventEmitter很接近,但正如你提到的,它只会向直接的父组件发出一个事件

在Angular中,还有其他的替代方案,我将在下面解释。

@Input()绑定允许在有向对象图中连接应用程序模型(根到叶)。组件的更改检测器策略的默认行为是将所有更改从任何连接的组件传播到所有绑定的应用程序模型。

旁白:有两种类型的模型:视图模型和应用程序模型。应用程序模型通过@Input()绑定连接。视图模型只是一个绑定在组件模板中的组件属性(没有@Input()装饰)。

回答你的问题:

如果我需要在兄弟组件之间通信怎么办?

    <李> < p > 共享应用程序模型: 兄弟姐妹可以通过共享的应用程序模型进行通信(就像angular 1一样)。例如,当一个兄弟姐妹对一个模型进行了更改时,另一个绑定到同一模型的兄弟姐妹就会自动更新
  1. 组件事件:子组件可以使用@Output()绑定向父组件发出事件。父组件可以处理事件,并操作应用程序模型或它自己的视图模型。对应用程序模型的更改将自动传播到直接或间接绑定到同一模型的所有组件。

  2. 服务活动:组件可以订阅服务事件。例如,两个兄弟组件可以订阅相同的服务事件,并通过修改各自的模型进行响应。下文将详细介绍。

我如何在根组件和嵌套了几个层次的组件之间进行通信?

  1. 共享应用程序模型:应用程序模型可以通过@Input()绑定从根组件传递到嵌套较深的子组件。任何组件对模型的更改都会自动传播到共享相同模型的所有组件。
  2. 服务活动:你也可以将EventEmitter移动到共享服务,这允许任何组件注入服务和订阅事件。这样,根组件就可以调用服务方法(通常会改变模型),而服务方法又会触发一个事件。再往下几层,也注入了服务并订阅了同一事件的子组件可以处理该事件。任何更改共享应用程序模型的事件处理程序都将自动传播到依赖于它的所有组件。这可能是Angular 1中最接近的$scope.broadcast()。下一节将更详细地描述这个想法。

使用服务事件传播更改的可观察服务示例

下面是一个可观察服务的例子,它使用服务事件传播更改。添加TodoItem时,服务发出事件通知其组件订阅者。

export class TodoItem {
constructor(public name: string, public done: boolean) {
}
}
export class TodoService {
public itemAdded$: EventEmitter<TodoItem>;
private todoList: TodoItem[] = [];


constructor() {
this.itemAdded$ = new EventEmitter();
}


public list(): TodoItem[] {
return this.todoList;
}


public add(item: TodoItem): void {
this.todoList.push(item);
this.itemAdded$.emit(item);
}
}

下面是根组件订阅事件的方式:

export class RootComponent {
private addedItem: TodoItem;
constructor(todoService: TodoService) {
todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
}


private onItemAdded(item: TodoItem): void {
// do something with added item
this.addedItem = item;
}
}

嵌套多层的子组件将以相同的方式订阅事件:

export class GrandChildComponent {
private addedItem: TodoItem;
constructor(todoService: TodoService) {
todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
}


private onItemAdded(item: TodoItem): void {
// do something with added item
this.addedItem = item;
}
}

下面是调用服务来触发事件的组件(它可以驻留在组件树中的任何位置):

@Component({
selector: 'todo-list',
template: `
<ul>
<li *ngFor="#item of model"> \{\{ item.name }}
</li>
</ul>
<br />
Add Item <input type="text" #txt /> <button (click)="add(txt.value); txt.value='';">Add</button>
`
})
export class TriggeringComponent{
private model: TodoItem[];


constructor(private todoService: TodoService) {
this.model = todoService.list();
}


add(value: string) {
this.todoService.add(new TodoItem(value, false));
}
}

参考:Angular中的变化检测

我在这里创建了一个pub-sub示例:

http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0

这个想法是使用RxJs subject来连接一个观察者和和可观察对象,作为一个通用的解决方案来发送和订阅自定义事件。在我的示例中,出于演示目的,我使用了一个customer对象

this.pubSubService.Stream.emit(customer);


this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));
这里是一个现场演示以及: http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub < / p >

下面的代码是Angular 2中使用共享服务来处理事件的$ scope.emit ()$ scope.broadcast ()的替换示例。

import {Injectable} from 'angular2/core';
import * as Rx from 'rxjs/Rx';


@Injectable()
export class EventsService {
constructor() {
this.listeners = {};
this.eventsSubject = new Rx.Subject();


this.events = Rx.Observable.from(this.eventsSubject);


this.events.subscribe(
({name, args}) => {
if (this.listeners[name]) {
for (let listener of this.listeners[name]) {
listener(...args);
}
}
});
}


on(name, listener) {
if (!this.listeners[name]) {
this.listeners[name] = [];
}


this.listeners[name].push(listener);
}


off(name, listener) {
this.listeners[name] = this.listeners[name].filter(x => x != listener);
}


broadcast(name, ...args) {
this.eventsSubject.next({
name,
args
});
}
}

使用示例:

广播:

function handleHttpError(error) {
this.eventsService.broadcast('http-error', error);
return ( Rx.Observable.throw(error) );
}

听众:

import {Inject, Injectable} from "angular2/core";
import {EventsService}      from './events.service';


@Injectable()
export class HttpErrorHandler {
constructor(eventsService) {
this.eventsService = eventsService;
}


static get parameters() {
return [new Inject(EventsService)];
}


init() {
this.eventsService.on('http-error', function(error) {
console.group("HttpErrorHandler");
console.log(error.status, "status code detected.");
console.dir(error);
console.groupEnd();
});
}
}

它可以支持多个参数:

this.eventsService.broadcast('something', "Am I a?", "Should be b", "C?");


this.eventsService.on('something', function (a, b, c) {
console.log(a, b, c);
});

我们实现了一个ngModelChange可观察指令,它通过你在自己的组件中实例化的事件发射器发送所有模型更改。您只需将事件发射器绑定到该指令。

看:https://github.com/atomicbits/angular2-modelchangeobservable

在html中,绑定你的事件发射器(本例中为countryChanged):

<input [(ngModel)]="country.name"
[modelChangeObservable]="countryChanged"
placeholder="Country"
name="country" id="country"></input>

在你的typescript组件中,对EventEmitter执行一些异步操作:

import ...
import {ModelChangeObservable} from './model-change-observable.directive'




@Component({
selector: 'my-component',
directives: [ModelChangeObservable],
providers: [],
templateUrl: 'my-component.html'
})


export class MyComponent {


@Input()
country: Country


selectedCountries:Country[]
countries:Country[] = <Country[]>[]
countryChanged:EventEmitter<string> = new EventEmitter<string>()




constructor() {


this.countryChanged
.filter((text:string) => text.length > 2)
.debounceTime(300)
.subscribe((countryName:string) => {
let query = new RegExp(countryName, 'ig')
this.selectedCountries = this.countries.filter((country:Country) => {
return query.test(country.name)
})
})
}
}

我正在使用一个消息服务,包装rxjs Subject (TypeScript)

< a href = " https://plnkr.co/edit/EjxffJLwTAusMI6BtEUw?p=preview" rel="noreferrer">活塞示例:Message Service . p=preview" rel="noreferrer">活塞示例:Message Service . p=preview" rel="noreferrer">

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/filter'
import 'rxjs/add/operator/map'


interface Message {
type: string;
payload: any;
}


type MessageCallback = (payload: any) => void;


@Injectable()
export class MessageService {
private handler = new Subject<Message>();


broadcast(type: string, payload: any) {
this.handler.next({ type, payload });
}


subscribe(type: string, callback: MessageCallback): Subscription {
return this.handler
.filter(message => message.type === type)
.map(message => message.payload)
.subscribe(callback);
}
}

组件可以订阅和广播事件(发送方):

import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'


@Component({
selector: 'sender',
template: ...
})
export class SenderComponent implements OnDestroy {
private subscription: Subscription;
private messages = [];
private messageNum = 0;
private name = 'sender'


constructor(private messageService: MessageService) {
this.subscription = messageService.subscribe(this.name, (payload) => {
this.messages.push(payload);
});
}


send() {
let payload = {
text: `Message ${++this.messageNum}`,
respondEvent: this.name
}
this.messageService.broadcast('receiver', payload);
}


clear() {
this.messages = [];
}


ngOnDestroy() {
this.subscription.unsubscribe();
}
}

(接收方)

import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'


@Component({
selector: 'receiver',
template: ...
})
export class ReceiverComponent implements OnDestroy {
private subscription: Subscription;
private messages = [];


constructor(private messageService: MessageService) {
this.subscription = messageService.subscribe('receiver', (payload) => {
this.messages.push(payload);
});
}


send(message: {text: string, respondEvent: string}) {
this.messageService.broadcast(message.respondEvent, message.text);
}


clear() {
this.messages = [];
}


ngOnDestroy() {
this.subscription.unsubscribe();
}
}

MessageServicesubscribe方法返回一个rxjs Subscription对象,可以像这样取消订阅:

import { Subscription } from 'rxjs/Subscription';
...
export class SomeListener {
subscription: Subscription;


constructor(private messageService: MessageService) {
this.subscription = messageService.subscribe('someMessage', (payload) => {
console.log(payload);
this.subscription.unsubscribe();
});
}
}

也可以看到这个答案:https://stackoverflow.com/a/36782616/1861779

< a href = " https://plnkr.co/edit/EjxffJLwTAusMI6BtEUw?p=preview" rel="noreferrer">活塞示例:Message Service . p=preview" rel="noreferrer">活塞示例:Message Service . p=preview" rel="noreferrer">

这是我的版本:

export interface IEventListenr extends OnDestroy{
ngOnDestroy(): void
}


@Injectable()
export class EventManagerService {




private listeners = {};
private subject = new EventEmitter();
private eventObserver = this.subject.asObservable();




constructor() {


this.eventObserver.subscribe(({name,args})=>{






if(this.listeners[name])
{
for(let listener of this.listeners[name])
{
listener.callback(args);
}
}
})


}


public registerEvent(eventName:string,eventListener:IEventListenr,callback:any)
{


if(!this.listeners[eventName])
this.listeners[eventName] = [];


let eventExist = false;
for(let listener of this.listeners[eventName])
{


if(listener.eventListener.constructor.name==eventListener.constructor.name)
{
eventExist = true;
break;
}
}


if(!eventExist)
{
this.listeners[eventName].push({eventListener,callback});
}
}


public unregisterEvent(eventName:string,eventListener:IEventListenr)
{


if(this.listeners[eventName])
{
for(let i = 0; i<this.listeners[eventName].length;i++)
{


if(this.listeners[eventName][i].eventListener.constructor.name==eventListener.constructor.name)
{
this.listeners[eventName].splice(i, 1);
break;
}
}
}




}




emit(name:string,...args:any[])
{
this.subject.next({name,args});
}
}

使用:

export class <YOURCOMPONENT> implements IEventListener{


constructor(private eventManager: EventManagerService) {




this.eventManager.registerEvent('EVENT_NAME',this,(args:any)=>{
....
})




}


ngOnDestroy(): void {
this.eventManager.unregisterEvent('closeModal',this)
}

发出:

 this.eventManager.emit("EVENT_NAME");

服务事件:组件可以订阅服务事件。例如,两个兄弟组件可以订阅相同的服务事件,并通过修改各自的模型进行响应。下文将详细介绍。

但是要确保在销毁父组件时取消订阅。

我最喜欢的方法是在我的服务中使用行为主题或事件发射器(几乎相同)来控制我的所有子组件。

使用angular cli,运行ng gs来创建一个新的服务,然后使用BehaviorSubject或EventEmitter

export Class myService {
#all the stuff that must exist


myString: string[] = [];
contactChange : BehaviorSubject<string[]> = new BehaviorSubject(this.myString);


getContacts(newContacts) {
// get your data from a webservices & when you done simply next the value
this.contactChange.next(newContacts);
}
}

当您这样做时,作为提供者使用您的服务的每个组件都将知道更改。只需订阅结果,就像你用eventEmitter;)

export Class myComp {
#all the stuff that exists like @Component + constructor using (private myService: myService)


this.myService.contactChange.subscribe((contacts) => {
this.contactList += contacts; //run everytime next is called
}
}

请勿使用 EventEmitter用于你的服务通信。

你应该使用Observable类型之一。我个人喜欢BehaviorSubject。

简单的例子:

你可以传递初始状态,这里我传递null

let subject = new BehaviorSubject(null);

当你想要更新主题时

subject.next (myObject)

观察任何服务或组件,并在其获得新的更新时采取行动。

subject.subscribe (this.YOURMETHOD);

这里有更多信息。