Wydarzenia na świecie w Angular

Czy nie ma odpowiednika $scope.emit() LUB $scope.broadcast() w Angular?

Znam funkcjonalność EventEmitter, ale z tego, co wiem, będzie to po prostu emitować Zdarzenie do nadrzędnego elementu HTML.

Co jeśli muszę się komunikować między fx. rodzeństwo czy między komponentem w korzeniu DOM a elementem zagnieżdżonym na kilku poziomach?
Author: amphetamachine, 2016-01-10

10 answers

Nie ma odpowiednika $scope.emit() lub $scope.broadcast() z AngularJS. EventEmitter wewnątrz komponentu zbliża się, ale jak już wspomnieliście, będzie emitować Zdarzenie tylko do najbliższego komponentu nadrzędnego.

W Angular, są inne alternatywy, które postaram się wyjaśnić poniżej.

@input() bindings umożliwia połączenie Modelu aplikacji w ukierunkowanym wykresie obiektu (root do leaves). Domyślnym zachowaniem strategii detektora zmian komponentu jest propagacja wszystkich zmian do model aplikacji dla wszystkich wiązań z dowolnego podłączonego komponentu.

Na bok: istnieją dwa rodzaje modeli: Modele widoku i modele aplikacji. Model aplikacji jest połączony za pomocą wiązań @Input (). Model widoku jest tylko właściwością komponentu (nie ozdobioną znakiem @Input ()), która jest związana w szablonie komponentu.

Aby odpowiedzieć na twoje pytania:

Co jeśli muszę komunikować się między komponentami rodzeństwa?

  1. Wspólna Aplikacja Model : Rodzeństwo może komunikować się za pomocą współdzielonego modelu aplikacji (podobnie jak angular 1). Na przykład, gdy jedno rodzeństwo zmienia model, drugie rodzeństwo, które ma powiązania z tym samym modelem, jest automatycznie aktualizowane.

  2. Zdarzenia komponentu: Komponenty potomne mogą emitować Zdarzenie do komponentu nadrzędnego za pomocą wiązań @ Output (). Komponent nadrzędny może obsługiwać zdarzenie i manipulować modelem aplikacji lub własnym modelem widoku. Zmiany w Modele aplikacji są automatycznie propagowane do wszystkich komponentów, które bezpośrednio lub pośrednio wiążą się z tym samym modelem.

  3. Zdarzenia Usługi : komponenty mogą subskrybować zdarzenia usługi. Na przykład dwa komponenty siostrzane mogą subskrybować to samo zdarzenie usługi i reagować, modyfikując swoje modele. Więcej na ten temat poniżej.

Jak mogę komunikować się między komponentem głównym a komponentem zagnieżdżonym na kilku poziomach głęboko?

  1. Shared Application Model : model aplikacji może być przekazany z głównego komponentu w dół do głęboko zagnieżdżonych pod-komponentów poprzez powiązania @Input (). Zmiany w modelu z dowolnego komponentu będą automatycznie propagowane do wszystkich komponentów, które mają ten sam model.
  2. zdarzenia Usługi : Można również przenieść EventEmitter do usługi współdzielonej, która umożliwia dowolnemu komponentowi wstrzyknięcie usługi i subskrypcję zdarzenia. W ten sposób korzeń komponent może wywołać metodę usługi (zazwyczaj mutującą model), która z kolei emituje Zdarzenie. Kilka warstw w dół, komponent grand-child, który również wstrzyknął usługę i subskrybował to samo zdarzenie, może ją obsłużyć. Każda obsługa zdarzeń, która zmienia model współdzielonej aplikacji, będzie automatycznie propagowana do wszystkich komponentów, które od niej zależą. Jest to prawdopodobnie najbliższy odpowiednik $scope.broadcast() z kąta 1. Następny rozdział opisuje ten pomysł w bardziej szczegóły.

Przykład Obserwowalnej usługi, która wykorzystuje zdarzenia usługi do propagowania zmian

Oto przykład obserwowalnej usługi, która używa zdarzeń usługi do propagowania zmian. Po dodaniu TodoItem usługa emituje Zdarzenie powiadamiające subskrybentów komponentu.

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

Oto jak komponent root zapisałby się do zdarzenia:

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

Komponent potomny zagnieżdżony na kilku głębokich poziomach subskrybowałby Zdarzenie w ten sam sposób:

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

Oto komponent, który wywołuje usługę w celu wywołania zdarzenia (może znajdować się w dowolnym miejscu w drzewie komponentów):

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

Referencja: wykrywanie zmian kątowych

 345
Author: pixelbits,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-16 21:36:41

Poniższy kod jako przykład zamiennika dla $ scope.emit () lub $ scope.broadcast () w Angular 2 za pomocą shared service do obsługi zdarzeń.

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

Przykładowe użycie:

Emisja:

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

Słuchacz:

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

Może obsługiwać wiele argumentów:

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
Author: jim.taylor.1974,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-29 00:06:22

Używam usługi wiadomości, która zawija rxjs Subject (TypeScript)

Przykład: 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);
  }
}

Komponenty mogą subskrybować i nadawać zdarzenia (nadawca):

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

(odbiorca)

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

Metoda subscribe MessageService zwraca obiekt rxjs Subscription, z którego można zrezygnować w następujący sposób:

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

Zobacz też tę odpowiedź: https://stackoverflow.com/a/36782616/1861779

Przykład Plunkera: wiadomość Serwis

 14
Author: t.888,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-09 08:32:51

Nie używaj EventEmitter do komunikacji z serwisem.

Powinieneś użyć jednego z obserwowalnych typów. Osobiście lubię BehaviorSubject.

Prosty przykład:

Możesz przekazać stan początkowy, tutaj przekazuję null

Let subject = new BehaviorSubject (null);

Kiedy chcesz zaktualizować temat

Temat.next (myObject)

Obserwować z dowolnego serwisu lub komponentu i działać, gdy dostaje nowe aktualizacje.

Temat.subscribe (this.YOURMETHOD);

Oto więcej informacji..

 11
Author: Danial Kalbasi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-24 03:31:36

Możesz użyć EventEmitter lub observables, aby utworzyć usługę eventbus, którą zarejestrujesz w DI. Każdy komponent, który chce uczestniczyć, żąda usługi jako parametru konstruktora i emituje i / lub subskrybuje zdarzenia.

Zobacz też

 9
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:32

Stworzyłem próbkę pub-sub tutaj:

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

Ideą jest użycie obiektów RxJs do podłączenia obserwatora i Obserwabli jako ogólnego rozwiązania do emitowania i subskrybowania niestandardowych zdarzeń. W mojej próbce używam obiektu Klienta do celów demonstracyjnych

this.pubSubService.Stream.emit(customer);

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

Tutaj jest również demo na żywo: http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub

 2
Author: TGH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-11 04:02:28

Moim ulubionym sposobem jest użycie emitera obiektu zachowania lub zdarzenia (prawie tego samego) w mojej usłudze, aby kontrolować cały mój subkomponent.

Używając angular cli, Uruchom ng G S, aby utworzyć nową usługę, a następnie użyj BehaviorSubject lub 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);
   }
}

Kiedy to zrobisz, każdy komponent korzystający z Twojej usługi jako dostawcy będzie świadomy tej zmiany. Po prostu zapisz się do wyniku, tak jak to robisz z 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
Author: Andrea Martines,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-19 14:46:40

Zaimplementowaliśmy obserwowalną dyrektywę ngModelChange, która wysyła wszystkie zmiany modelu za pośrednictwem emitera zdarzeń, który tworzysz we własnym komponencie. Po prostu musisz powiązać emiter zdarzeń z dyrektywą.

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

W html, bind your event emitter (countryChanged w tym przykładzie):

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

W komponencie typescript wykonaj kilka operacji asynchronicznych na Eventemitterze:

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
Author: Peter Rigole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 11:42:45

To jest moja wersja:

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

Użycie:

export class <YOURCOMPONENT> implements IEventListener{

  constructor(private eventManager: EventManagerService) {


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


  }

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

}

Emitować:

 this.eventManager.emit("EVENT_NAME");
 1
Author: zakrzu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-26 10:55:53

Zdarzenia Usługi: komponenty mogą subskrybować zdarzenia usługi. Na przykład dwa komponenty siostrzane mogą subskrybować to samo zdarzenie usługi i reagować, modyfikując swoje modele. Więcej na ten temat poniżej.

Ale pamiętaj, aby wypisać się z tego na destroy komponentu nadrzędnego.

 0
Author: B.V.S Bharat Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 20:11:36