Angular中没有等价的$scope.emit()或$scope.broadcast()吗?
我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。
如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?
Angular中没有等价的$scope.emit()或$scope.broadcast()吗?
我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。
如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?
您可以使用EventEmitter或可观察对象来创建向DI注册的事件总线服务。每个想要参与的组件只需将服务作为构造函数参数请求,并发出和/或订阅事件。
另请参阅
https://angular.io/docs/ts/latest/cookbook/component-communication.html !# bidirectional-service 委托:在Angular2中的EventEmitter或Observable
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中的变更检测
我在这里创建了一个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);
});
我们实现了一个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主题(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
这是我的版本:
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});
}
}
use:
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);
当你想要更新主题时
next (myObject)科目。
观察任何服务或组件,并在其获得新的更新时采取行动。
subject.subscribe (this.YOURMETHOD);
这里有更多信息。