有没有相当于$scope.emit()
或$scope.broadcast()
在角?
我知道该EventEmitter
功能,但是据我了解,这只会向父HTML元素发出一个事件。
如果我需要在FX之间进行通信该怎么办。兄弟姐妹还是DOM根目录中的组件与嵌套在多个级别中的元素之间?
有没有相当于$scope.emit()
或$scope.broadcast()
在角?
我知道该EventEmitter
功能,但是据我了解,这只会向父HTML元素发出一个事件。
如果我需要在FX之间进行通信该怎么办。兄弟姐妹还是DOM根目录中的组件与嵌套在多个级别中的元素之间?
Answers:
没有等同于$scope.emit()
或$scope.broadcast()
来自AngularJS。组件内部的EventEmitter接近,但是正如您提到的,它只会向直接父组件发送一个事件。
在Angular中,我将在下面尝试解释其他替代方法。
@Input()绑定允许将应用程序模型连接到有向对象图(从根到叶)。组件的更改检测器策略的默认行为是将所有更改传播到来自任何已连接组件的所有绑定的应用程序模型。
除了:有两种类型的模型:视图模型和应用程序模型。应用程序模型通过@Input()绑定进行连接。视图模型只是组件属性(未使用@Input()装饰),该属性绑定在组件模板中。
要回答您的问题:
如果需要在兄弟组件之间进行通信怎么办?
共享应用程序模型:兄弟姐妹可以通过共享应用程序模型进行通信(就像angular 1一样)。例如,当一个同级对模型进行更改时,具有相同模型绑定的另一同级会自动更新。
组件事件:子组件可以使用@Output()绑定将事件发送给父组件。父组件可以处理事件,并操纵应用程序模型或它自己的视图模型。对应用程序模型的更改将自动传播到直接或间接绑定到同一模型的所有组件。
服务事件:组件可以订阅服务事件。例如,两个同级组件可以订阅相同的服务事件,并通过修改它们各自的模型进行响应。在下面的更多内容。
如何在根组件和嵌套多个级别的组件之间进行通信?
$scope.broadcast()
Angular 1 最接近。使用服务事件传播更改的可观察服务的示例
这是使用服务事件传播更改的可观察服务的示例。添加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));
}
}
参考:角度变化检测
itemAdded$
。那是RxJS约定吗?这是从哪里来的?
street
应用程序模型属性的组件示例,但是由于Angular 2通过身份/引用实现更改检测,因此不会传播(onChanges
不调用)更改,因为应用程序模型引用未更改(续...)
以下代码作为使用共享服务处理事件来替换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);
});
off(name, listener) { this.listeners[name] = this.listeners[name].filter(x => x != listener); }
我正在使用包装rxjs Subject
(TypeScript)的消息服务
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();
}
}
返回rxjs 对象的subscribe
方法,可以像这样取消订阅该对象:MessageService
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
Property 'filter' does not exist on type 'Subject<EventMessage>'.
return this.handler.pipe( filter(message => message.type === type), map(message => message.payload) ).subscribe(callback);
不要将 EventEmitter用于服务通信。
您应该使用Observable类型之一。我个人喜欢BehaviorSubject。
简单的例子:
您可以传递初始状态,在这里我传递null
让subject = new BehaviorSubject(null);
当您想更新主题时
subject.next(myObject)
观察任何服务或组件,并在获得新更新时采取行动。
subject.subscribe(this.YOURMETHOD);
您可以使用EventEmitter或 Observables创建您在DI中注册的事件总线服务。每个想要参与的组件都只是将服务请求为构造函数参数,并发出和/或订阅事件。
也可以看看
我最喜欢的方法是在服务中使用行为主题或事件发射器(几乎相同)来控制我的所有子组件。
使用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
}
}
我在这里创建了一个发布订阅示例:
http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0
这个想法是使用RxJs主题连接一个Observer和Observables,作为发出和订阅自定义事件的通用解决方案。在我的示例中,我使用客户对象进行演示
this.pubSubService.Stream.emit(customer);
this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));
这也是一个现场演示:http : //www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub
这是我的版本:
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");
我们实现了ngModelChange observable指令,该指令通过您在自己的组件中实例化的事件发射器发送所有模型更改。您只需要将事件发射器绑定到指令。
参见:https : //github.com/atomicbits/angular2-modelchangeobservable
在html中,绑定事件发射器(在此示例中为countryChanged):
<input [(ngModel)]="country.name"
[modelChangeObservable]="countryChanged"
placeholder="Country"
name="country" id="country"></input>
在您的打字稿组件中,对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)
})
})
}
}
服务事件:组件可以订阅服务事件。例如,两个同级组件可以订阅相同的服务事件,并通过修改其各自的模型进行响应。在下面的更多内容。
但是,请确保在取消父组件时取消订阅。