首页 文章

Angular中的全球事件

提问于
浏览
197

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

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

如果我需要在fx之间进行通信怎么办?兄弟姐妹或DOM根目录中的组件和嵌套在几个级别的元素?

10 回答

  • 9

    没有相当于AngularJS的 $scope.emit()$scope.broadcast() . 组件内部的EventEmitter接近,但正如您所提到的,它只会向直接父组件发出一个事件 .

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

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

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

    回答你的问题:

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

    • Shared Application Model :兄弟姐妹可以通过共享应用程序模型进行通信(就像角度1一样) . 例如,当一个兄弟对模型进行更改时,另一个绑定到同一模型的兄弟会自动更新 .

    • Component Events :子组件可以使用@Output()绑定向父组件发出事件 . 父组件可以处理事件,并操纵应用程序模型或它自己的视图模型 . 对应用程序模型的更改会自动传播到直接或间接绑定到同一模型的所有组件 .

    • Service Events :组件可以订阅服务事件 . 例如,两个兄弟组件可以订阅相同的服务事件,并通过修改其各自的模型进行响应 . 更多关于此的信息 .

    如何在Root组件和嵌套多个级别的组件之间进行通信?

    • Shared Application Model :应用程序模型可以通过@Input()绑定从Root组件传递到深层嵌套的子组件 . 从任何组件对模型的更改将自动传播到共享相同模型的所有组件 .

    • Service Events :您还可以将EventEmitter移动到共享服务,该服务允许任何组件注入服务并订阅事件 . 这样,Root组件可以调用服务方法(通常是改变模型),然后发出一个事件 . 几个层向下,一个也注入了服务并订阅同一事件的grand-child组件可以处理它 . 任何更改共享应用程序模型的事件处理程序都将自动传播到依赖于它的所有组件 . 这可能是与Angular 1中最接近的 $scope.broadcast() . 下一节将更详细地描述这个想法 .

    Example of an Observable Service that uses Service Events to Propagate Changes

    以下是使用服务事件传播更改的可观察服务的示例 . 添加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>
            
    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)); } }

    参考:Change Detection in Angular

  • 44

    以下代码作为Angular 2中 $scope.emit()$scope.broadcast() 的替换示例,使用 shared service 来处理事件 .

    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);
        }
    
        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);
    });
    
  • 15

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

    Plunker example: 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();
      }
    }
    

    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

    Plunker example: Message Service

  • 351

    DO Not Use EventEmitter用于您的服务通信 .

    您应该使用Observable类型之一 . 我个人喜欢BehaviorSubject .

    Simple example:

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

    let subject = new BehaviorSubject(null);

    当您想要更新主题时

    subject.next(myObject)

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

    subject.subscribe(this.YOURMETHOD);

    Here is more information. .

  • 2

    您可以使用EventEmitter或observables创建您在DI中注册的eventbus服务 . 每个想要参与的组件只是将服务作为构造函数参数请求并发出和/或订阅事件 .

    也可以看看

  • 1

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

    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

  • 0

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

    使用angular cli,运行ng s以创建新服务,然后使用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
      }
    }
    
  • 1

    我们实现了一个ngModelChange observable指令,该指令通过您在自己的组件中实例化的事件 Launcher 发送所有模型更改 . 您只需将事件 Launcher 绑定到指令即可 .

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

    在html中,绑定您的事件 Launcher (在此示例中为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)
                    })
                })
        }
    }
    
  • 2

    这是我的版本:

    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");
    
  • 11

    服务事件:组件可以订阅服务事件 . 例如,两个兄弟组件可以订阅相同的服务事件,并通过修改其各自的模型进行响应 . 更多关于此的信息 .

    但请确保在销毁父组件时取消订阅 .

相关问题