Глобальные события в Angular



нет, эквивалентной $scope.emit() или $scope.broadcast() в угловой?



Я знаю EventEmitter функциональность, но, насколько я понимаю, это просто вызовет событие для родительского элемента HTML.



Что делать, если мне нужно общаться между fx. братья и сестры или между компонентом в корне DOM и элементом, вложенным на несколько уровней глубже?

676   10  

10 ответов:

нет эквивалента $scope.emit() или

следующий код в качестве примера для замены $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 возвращает 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 пример: служба сообщений

не использовать 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

    Ничего не найдено.