Глобальные события в Angular
нет, эквивалентной $scope.emit() или $scope.broadcast() в угловой?
Я знаю EventEmitter функциональность, но, насколько я понимаю, это просто вызовет событие для родительского элемента HTML.
Что делать, если мне нужно общаться между fx. братья и сестры или между компонентом в корне DOM и элементом, вложенным на несколько уровней глубже?
10 ответов:
следующий код в качестве примера для замены $scope.выделяют() или $scope.трансляция () в угловой 2 с помощью общая служба для обработки событий.
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); });
я использую службу сообщений, которая обертывает rxjs
Subject(машинопись)Plunker пример: служба сообщений
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(); } }The
subscribeметодMessageServiceвозвращает rxjsSubscriptionобъект, от которого можно отписаться вот так: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
не использовать EventEmitter для связи.
вы должны использовать один из наблюдаемых типов. Мне лично нравится BehaviorSubject.
простой пример:
вы можете передать начальное состояние, здесь я передаю null
пусть subject = new BehaviorSubject (null);
когда вы хотите обновить тему
тема.далее (myObject)
наблюдать из любого сервиса или компонента и действовать, когда он получает новые обновления.
тема.подписаться(на это.YOURMETHOD);
можно использовать
EventEmitter илиobservables для создания службы eventbus, которая регистрируется в DI. Каждый компонент, который хочет участвовать, просто запрашивает службу в качестве параметра конструктора и выдает и/или подписывается на события.см. также
Я создал паб-суб образец здесь:
http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0
идея состоит в том, чтобы использовать субъекты RxJs для подключения наблюдателя и наблюдаемых объектов в качестве общего решения для излучения и подписки на пользовательские события. В моем примере я использую объект 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 cli, запустите ng g 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 } }
мы реализовали наблюдаемую директиву 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) }) }) } }
Это моя версия:
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");
сервисные события: компоненты могут подписываться на сервисные события. Например, два одноуровневых компонента могут подписаться на одно и то же событие службы и откликнуться, изменив свои соответствующие модели. Подробнее об этом ниже.
но обязательно отпишитесь на это при уничтожении родительского компонента.
Comments