Des Événements mondiaux Angulaire

n'y a-t-il pas D'équivalent de $scope.emit() ou $scope.broadcast() en angle?

je connais la fonctionnalité EventEmitter , mais pour autant que je comprenne que cela va juste émettre un événement à l'élément HTML parent.

Que faire si je dois communiquer entre fx. fratrie ou entre un composant de la racine du DOM et un élément niché à plusieurs niveaux de profondeur?

189
demandé sur amphetamachine 2016-01-10 03:03:20

10 réponses

il n'y a pas d'équivalent de $scope.emit() ou $scope.broadcast() de AngularJS. EventEmitter à l'intérieur d'un composant est proche, mais comme vous l'avez mentionné, il n'émettent qu'un événement immédiat composant parent.

en angle, il y a d'autres alternatives que je vais essayer d'expliquer ci-dessous.

@Input() bindings permet de connecter le modèle d'application dans un graphe objet dirigé (racine à feuilles). Le comportement par défaut d'un composant la stratégie du détecteur de changement est de propager toutes les modifications à un modèle d'application pour toutes les fixations de n'importe quel composant connecté.

mis à part: il existe deux types de Modèles: voir les modèles et les modèles D'Application. Un modèle d'application est connecté par le biais de fixations @Input (). Un modèle de vue est une propriété juste un composant (non décoré avec @Input ()) qui est liée dans le modèle du composant.

pour répondre à vos questions:

et si je besoin de communiquer entre frère composants?

  1. Modèle D'Application Partagée : Les frères et sœurs peuvent communiquer par le biais d'un modèle d'application partagé (tout comme angular 1). Par exemple, lorsqu'un frère ou une sœur fait une modification à un modèle, l'autre frère ou la sœur qui a des fixations sur le même modèle est automatiquement mis à jour.

  2. les Événements du Composant : les composants Enfants peut émettre un événement vers le composant parent en utilisant les fixations @Output (). Le composant parent peut gérer l'événement, et manipuler le modèle d'application ou son propre modèle de vue. Les modifications apportées au modèle de demande sont automatiquement propagées à tous les composants qui se lient directement ou indirectement au même modèle.

  3. Événements de Service : les Composants peuvent s'abonner à des événements de service. Par exemple, deux composants frères peuvent s'abonner au même événement de service et répondre en modifiant leurs modèles respectifs. En savoir plus sur ce ci-dessous.

Comment puis-je communiquer entre une composante racine et une composante imbriquée à plusieurs niveaux?

  1. "Shared Application Model : le modèle d'application peut être passé de la composante racine aux sous-composantes profondément imbriquées par le biais de fixations @Input (). Modifications apportées à un modèle à partir de n'importe quel composant seront automatiquement propagées à tous les composants qui partagent le même modèle.
  2. Service Events : vous pouvez également déplacer L'EventEmitter vers un service partagé, ce qui permet à n'importe quel composant d'injecter le service et de s'abonner à l'événement. De cette façon, un composant racine peut appeler une méthode de service (généralement la mutation du modèle), qui à son tour émet un événement. Plusieurs couches vers le bas, une composante de petit-enfant qui a également injecté le service et souscrit à la un même événement peut s'en occuper. Tout gestionnaire d'événements qui modifie un modèle d'Application partagé se propagera automatiquement à tous les composants qui en dépendent. C'est probablement l'équivalent le plus proche de $scope.broadcast() de l'Angulaire 1. La section suivante décrit cette idée plus en détail.

exemple de Service Observable qui utilise des événements de Service pour propager des changements

voici un exemple de service observable qui utilise des événements de service pour propager des changements. Lorsqu'un TodoItem est ajouté, le service émet un événement avisant ses abonnés.

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

Voici comment un composant root s'abonnerait à l'événement:

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;
    }
}

un composant enfant imbriqué plusieurs niveaux profonds souscrirait à l'événement de la même manière:

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;
    }
}

Ici est le composant qui appelle le service pour déclencher l'événement (il peut résider n'importe où dans l'arbre des composants):

@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));
    }
}

référence: détection de changement angulaire

345
répondu pixelbits 2017-12-16 21:36:41

le code suivant comme exemple de remplacement de $scope.emit() ou $champ d'application.broadcast () en angle 2 en utilisant un service partagé pour gérer les événements.

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

exemple d'usage:

diffusion:

function handleHttpError(error) {
    this.eventsService.broadcast('http-error', error);
    return ( Rx.Observable.throw(error) );
}

auditeur:

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

Il peut prendre en charge plusieurs arguments:

this.eventsService.broadcast('something', "Am I a?", "Should be b", "C?");

this.eventsService.on('something', function (a, b, c) {
   console.log(a, b, c);
});
41
répondu jim.taylor.1974 2016-03-29 00:06:22

j'utilise un service de messagerie qui enveloppe un rxjs Subject (dactylographié)

exemple de 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);
  }
}
Les composantes

peuvent s'abonner et diffuser des événements (expéditeur):

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

(récepteur)

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

la méthode subscribe de MessageService renvoie un objet rxjs Subscription , qui peut être désabonné de la même façon:

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

Voir Aussi cette réponse: https://stackoverflow.com/a/36782616/1861779

exemple de Plunker: Message Service

14
répondu t.888 2018-06-09 08:32:51

N'utilisez pas EventEmitter pour votre communication de service.

vous devez utiliser l'un des types observables. J'aime personnellement BehaviorSubject.

exemple Simple:

vous pouvez passer l'état initial, ici je passe null

laissez l'objet = new BehaviorSubject(null);

quand vous voulez mettre à jour le sujet

sujet.suivant (myObject)

Observez depuis n'importe quel service ou composant et agissez quand il reçoit de nouvelles mises à jour.

sujet.abonnez-vous(c'.VOTREMÉTHODE);

Voici plus d'informations. .

11
répondu Danial Kalbasi 2017-07-24 03:31:36

vous pouvez utiliser EventEmitter ou observables pour créer un service eventbus que vous enregistrez avec DI. Chaque composant qui veut participer demande simplement le service comme paramètre constructeur et émet et/ou s'abonne à des événements.

Voir aussi

9
répondu Günter Zöchbauer 2017-05-23 12:18:32

j'ai créé un sous-échantillon de pub ici:

http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0

l'idée est d'utiliser des sujets RxJs pour connecter un observateur et et Observables comme une solution générique pour l'émission et l'abonnement à des événements personnalisés. Dans mon échantillon j'utilise un objet client à des fins de démonstration

this.pubSubService.Stream.emit(customer);

this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));

voici aussi une démo en direct: http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub

2
répondu TGH 2016-01-11 04:02:28

ma façon préférée de faire est d'utiliser le sujet de comportement ou l'émetteur d'événement (presque le même) dans mon service pour contrôler tous mes sous-composants.

en utilisant angular cli, exécuter ng G S pour créer un nouveau service puis utiliser un BehaviorSubject ou 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);
   }
}

lorsque vous faites que chaque composant utilisant votre service en tant que fournisseur sera au courant du changement. Il suffit de souscrire au résultat comme vous le faites avec 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
  }
}
2
répondu Andrea Martines 2017-07-19 14:46:40

nous avons implémenté une directive ngmodelchange observable qui envoie toutes les modifications du modèle par l'intermédiaire d'un émetteur d'événements que vous instanciez dans votre propre composant. Vous devez simplement lier votre émetteur d'événements à la directive.

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

en html, liez votre émetteur d'événements (représenté dans cet exemple):

<input [(ngModel)]="country.name"
       [modelChangeObservable]="countryChanged" 
       placeholder="Country"
       name="country" id="country"></input>

dans votre manuscrit composant, faire des opérations asynchrones sur le 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)
                })
            })
    }
}
1
répondu Peter Rigole 2016-07-28 11:42:45

C'est ma version:

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

utiliser:

export class <YOURCOMPONENT> implements IEventListener{

  constructor(private eventManager: EventManagerService) {


    this.eventManager.registerEvent('EVENT_NAME',this,(args:any)=>{
       ....
    })


  }

  ngOnDestroy(): void {
    this.eventManager.unregisterEvent('closeModal',this)
  }

}

emit:

 this.eventManager.emit("EVENT_NAME");
1
répondu zakrzu 2017-01-26 10:55:53

Événements de Service: les Composants peuvent s'abonner à des événements de service. Par exemple, deux composantes sœurs peuvent s'abonner au même événement de service et répondre en modifiant leurs modèles respectifs. En savoir plus sur ce ci-dessous.

Mais assurez-vous de vous désabonner à détruire le composant parent.

0
répondu B.V.S Bharat Kumar 2017-04-28 20:11:36