Jak zasubskrybować wydarzenie w usłudze w Angular2?

112

Wiem, jak podnieść zdarzenie za pomocą EventEmitter. Mogę również dołączyć metodę do wywołania, jeśli mam taki komponent:

<component-with-event (myevent)="mymethod($event)" />

Kiedy mam taki komponent, wszystko działa świetnie. Przeniosłem trochę logiki do usługi i muszę wywołać zdarzenie z wnętrza usługi. Oto co zrobiłem:

export class MyService {
  myevent: EventEmitter = new EventEmitter();

  someMethodThatWillRaiseEvent() {
    this.myevent.next({data: 'fun'});
  }
}

Mam komponent, który musi zaktualizować jakąś wartość na podstawie tego zdarzenia, ale nie wydaje mi się, aby działał. Próbowałem tego:

//Annotations...
export class MyComponent {
  constructor(myService: MyService) {
    //myService is injected properly and i already use methods/shared data on this.
    myService.myevent.on(... // 'on' is not a method <-- not working
    myService.myevent.subscribe(.. // subscribe is not a method <-- not working
  }
}

Jak sprawić, by MyComponent zasubskrybował zdarzenie, gdy usługa, która go zgłasza, nie jest składnikiem?

Jestem na 2.0.0-alpha.28

EDYCJA: Zmodyfikowałem mój „działający przykład”, aby faktycznie działał, więc można skupić się na niedziałającej części;)

Przykładowy kod: http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv

Per Hornshøj-Schierbeck
źródło
1
Może to być bardziej kwestia projektu, ponieważ usługa nie powinna emitować zdarzeń w imieniu komponentu, ponieważ to ściśle wiąże usługę z komponentem. na przykład co, jeśli istnieje wiele instancji komponentu, czy w takim przypadku wszystkie powinny emitować zdarzenia?
Uniwersytet Angular,
@jhadesdev Czytałem cię. Przeprojektowałem rozwiązanie, aby usługa nie musiała już emitować wyniku. Nadal uważam, że niektóre projekty przydałyby się, gdyby były w stanie podnosić wydarzenia - w zależności od tego, jaki rodzaj „usługi” to jest ...
Per Hornshøj-Schierbeck
jednym ze sposobów mogłoby być utworzenie przez komponent emitera zdarzeń zamiast usługi, a następnie przekazanie emitera zdarzeń jako argumentu do usługi
Angular University,

Odpowiedzi:

135

Aktualizacja : znalazłem lepszy / właściwy sposób rozwiązania tego problemu za pomocą BehaviorSubject lub Observable, a nie EventEmitter. Zobacz tę odpowiedź: https://stackoverflow.com/a/35568924/215945

Ponadto dokumenty Angular mają teraz przykład książki kucharskiej, który używa tematu .


Oryginalna / nieaktualna / zła odpowiedź: znowu nie używaj EventEmittera w usłudze . To jest anty-wzór.

Używając beta.1 ... NavService zawiera EventEmiter. Nawigacja po komponentach emituje zdarzenia za pośrednictwem usługi, a komponent ObservingComponent subskrybuje zdarzenia.

nav.service.ts

import {EventEmitter} from 'angular2/core';
export class NavService {
  navchange: EventEmitter<number> = new EventEmitter();
  constructor() {}
  emitNavChangeEvent(number) {
    this.navchange.emit(number);
  }
  getNavChangeEmitter() {
    return this.navchange;
  }
}

komponenty.ts

import {Component} from 'angular2/core';
import {NavService} from '../services/NavService';

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number = 0;
  subscription: any;
  constructor(private navService:NavService) {}
  ngOnInit() {
    this.subscription = this.navService.getNavChangeEmitter()
      .subscribe(item => this.selectedNavItem(item));
  }
  selectedNavItem(item: number) {
    this.item = item;
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

@Component({
  selector: 'my-nav',
  template:`
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
  `,
})
export class Navigation {
  item = 1;
  constructor(private navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this.navService.emitNavChangeEvent(item);
  }
}

Plunker

Mark Rajcok
źródło
Przestrzegałem tego, ale działa to tylko dla samej klasy. To nie działa dla innej klasy (z innego pliku). Wygląda na to, że problem dotyczy instancji klasy usług. Czy jest w ogóle, aby klasa usługi stała się statyczna?
asubanovsky
5
@asubanovsky, w plunkerze wstrzykuję NavService tylko na poziomie bootstrap (tj. komponentu głównego), więc powinno być tylko jedno wystąpienie usługi dla całej aplikacji. Czy wstrzykujesz to gdzie indziej providers: [NavService]? Jeśli tak, otrzymasz wiele wystąpień usługi.
Mark Rajcok
Dziękuję Ci bardzo. Teraz działa zgodnie z moimi oczekiwaniami. Teraz rozumiem różnicę między wstrzykiwaniem dostawców na poziomie bootstrap i na poziomie komponentów.
asubanovsky
Ten post został poważnie zmieniony do stanu, w którym już go nie rozumiem. Dlaczego wywołanie mehtoda w usłudze .getNavChangeEmitter (), która zwraca EventEmitter, różni się od bezpośredniej subskrypcji navChange?
Per Hornshøj-Schierbeck
@ PerHornshøj-Schierbeck, użyłem metody getNavChangeEmitter (), aby ukryć fakt, że EventEmitter był używany wewnątrz usługi. Użytkownicy metody mogliby założyć, że zwracany obiekt ma subscribe()metodę. Korzystając z metody, możemy łatwo zmienić EventEmitter na Subject lub Observable, co jest znacznie lepsze, ponieważ teraz nauczyliśmy się, że EventEmitters powinny być używane tylko dla właściwości wyjściowych. Właściwym sposobem jest użycie obiektu Subject, BehaviorSubject lub Observable i zwykle bezpośrednio subskrybujemy te elementy, dlatego nie potrzebujemy już metody getNavChangeEmitter ().
Mark Rajcok
6

Używając alfa 28, zrealizowałem programową subskrypcję emiterów zdarzeń za pomocą eventEmitter.toRx().subscribe(..)metody. Ponieważ nie jest to intuicyjne, może ulec zmianie w przyszłej wersji.

Runeboy
źródło
1
być może podasz prosty przykład do wykorzystania przez ludzi, jeśli natkną się na ten post? Przy okazji to nie ja przegłosowałem;)
Per Hornshøj-Schierbeck
2
Nie wiem, dlaczego głosowano w dół, na pewno działa. Mówiąc najprościej, aby zasubskrybować emiter wydarzenia „kliknij”, użyj click.toRx().subscribe(your_callback_function)
Runeboy,
Myślę, że mogło to zostać odrzucone, ponieważ brakuje mu działającego przykładu? Wiem, że będzie wart upvote a może akceptowanym odpowiedź gdyby albo link do pluncker / jsfiddle skrzypce czy nawet niektórych linii kodu, aby wyświetlić składnię lepiej;)
Per Hornshøj-Schierbeck
Otrzymuję WYJĄTEK: Błąd odniesienia: kliknięcie nie jest zdefiniowane @Runeboy Czy możesz podać plik plnkr dla swojego rozwiązania?
Benjamin McFerren
Musisz zdefiniować EventEmitter o nazwie click, aby działał
Mazen Elkashef