Angular中没有等价的$scope.emit()或$scope.broadcast()吗?
我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。
如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?
Angular中没有等价的$scope.emit()或$scope.broadcast()吗?
我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。
如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?
当前回答
我正在使用一个消息服务,包装rxjs主题(TypeScript)
Plunker示例:Message Service
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();
}
}
MessageService的subscribe方法返回一个rxjs订阅对象,可以像这样取消订阅:
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
Plunker示例:Message Service
其他回答
我正在使用一个消息服务,包装rxjs主题(TypeScript)
Plunker示例:Message Service
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();
}
}
MessageService的subscribe方法返回一个rxjs订阅对象,可以像这样取消订阅:
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
Plunker示例:Message Service
我在这里创建了一个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
下面的代码是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);
});
不要使用EventEmitter来进行服务通信。
你应该使用Observable类型之一。我个人喜欢BehaviorSubject。
简单的例子:
你可以传递初始状态,这里我传递null
let subject = new BehaviorSubject(null);
当你想要更新主题时
next (myObject)科目。
观察任何服务或组件,并在其获得新的更新时采取行动。
subject.subscribe (this.YOURMETHOD);
这里有更多信息。
AngularJS中没有等价的$scope.emit()或$scope.broadcast()。 组件内部的EventEmitter很接近,但正如您所提到的,它只会向直接的父组件发出事件。
在Angular中,还有其他的替代方案,我将在下面解释。
@Input()绑定允许在有向对象图中连接应用程序模型(根到叶)。组件的更改检测器策略的默认行为是将所有更改从任何连接的组件传播到所有绑定的应用程序模型。
旁白:有两种类型的模型:视图模型和应用程序模型。应用程序模型通过@Input()绑定连接。视图模型只是一个绑定在组件模板中的组件属性(没有@Input()装饰)。
回答你的问题:
如果我需要在兄弟组件之间通信怎么办?
Shared Application Model: Siblings can communicate through a shared application model (just like angular 1). For example, when one sibling makes a change to a model, the other sibling that has bindings to the same model is automatically updated. Component Events: Child components can emit an event to the parent component using @Output() bindings. The parent component can handle the event, and manipulate the application model or its own view model. Changes to the Application Model are automatically propagated to all components that directly or indirectly bind to the same model. Service Events: Components can subscribe to service events. For example, two sibling components can subscribe to the same service event and respond by modifying their respective models. More on this below.
我如何在根组件和嵌套了几个层次的组件之间进行通信?
Shared Application Model: The application model can be passed from the Root component down to deeply nested sub-components through @Input() bindings. Changes to a model from any component will automatically propagate to all components that share the same model. Service Events: You can also move the EventEmitter to a shared service, which allows any component to inject the service and subscribe to the event. That way, a Root component can call a service method (typically mutating the model), which in turn emits an event. Several layers down, a grand-child component which has also injected the service and subscribed to the same event, can handle it. Any event handler that changes a shared Application Model, will automatically propagate to all components that depend on it. This is probably the closest equivalent to $scope.broadcast() from Angular 1. The next section describes this idea in more detail.
使用服务事件传播更改的可观察服务示例
下面是一个可观察服务的例子,它使用服务事件传播更改。添加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中的变更检测