Wie abonniere ich eine Veranstaltung für einen Dienst in Angular2?

112

Ich weiß, wie man mit dem EventEmitter ein Ereignis auslöst. Ich kann auch eine aufzurufende Methode anhängen, wenn ich eine Komponente wie diese habe:

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

Wenn ich eine solche Komponente habe, funktioniert alles hervorragend. Ich habe eine Logik in einen Dienst verschoben und muss ein Ereignis innerhalb des Dienstes auslösen. Was ich getan habe war folgendes:

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

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

Ich habe eine Komponente, die einen Wert basierend auf diesem Ereignis aktualisieren muss, aber ich kann nicht scheinen, dass es funktioniert. Was ich versucht habe war folgendes:

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

Wie kann ich MyComponent dazu bringen, das Ereignis zu abonnieren, wenn der Dienst, der es auslöst, keine Komponente ist?

Ich bin auf 2.0.0-alpha.28

BEARBEITEN: Mein "Arbeitsbeispiel" wurde so geändert, dass es tatsächlich funktioniert, sodass der Fokus auf den nicht funktionierenden Teil gelegt werden kann;)

Beispielcode: http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv

Per Hornshøj-Schierbeck
quelle
1
Es könnte eher ein Problem des Designs sein, da ein Dienst keine Ereignisse für eine Komponente ausgeben sollte, da dies den Dienst eng mit der Komponente koppelt. Was ist beispielsweise, wenn mehrere Instanzen der Komponente vorhanden sind, sollten alle in diesem Fall Ereignisse ausgeben?
Angular University
@jhadesdev Ich habe dich gelesen. Ich habe die Lösung neu gestaltet, damit der Dienst das Ergebnis nicht mehr ausgeben muss. Ich denke immer noch, dass einige Entwürfe davon profitieren würden, Ereignisse auslösen zu können - je nachdem, um welche Art von "Service" es sich handelt ...
Per Hornshøj-Schierbeck
Eine Möglichkeit könnte dann sein, dass die Komponente den Ereignisemitter anstelle des Dienstes erstellt und dann den Ereignisemitter als Argument an den Dienst
übergibt

Antworten:

135

Update : Ich habe einen besseren / richtigen Weg gefunden, um dieses Problem mit einem BehaviorSubject oder einem Observable anstelle eines EventEmitter zu lösen. Bitte lesen Sie diese Antwort: https://stackoverflow.com/a/35568924/215945

Außerdem haben die Angular-Dokumente jetzt ein Kochbuchbeispiel, das einen Betreff verwendet .


Ursprüngliche / veraltete / falsche Antwort: Verwenden Sie erneut keinen EventEmitter in einem Dienst . Das ist ein Anti-Muster.

Verwenden von Beta.1 ... NavService enthält den EventEmiter. Die Komponentennavigation sendet Ereignisse über den Dienst, und die Komponente ObservingComponent abonniert die Ereignisse.

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

Komponenten.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
quelle
Ich habe dies befolgt, aber es funktioniert nur für die Klasse selbst. Es funktioniert nicht für eine andere Klasse (aus einer anderen Datei). Es scheint, dass das Problem in der Serviceklasseninstanz liegt. Gibt es überhaupt eine Möglichkeit, die Serviceklasse statisch zu machen?
Asubanovsky
5
@asubanovsky, im Plunker injiziere ich NavService nur auf Bootstrap-Ebene (dh Root-Komponente), daher sollte es nur eine Instanz des Dienstes für die gesamte App geben. Spritzen Sie es woanders ein providers: [NavService]? In diesem Fall erhalten Sie mehrere Instanzen des Dienstes.
Mark Rajcok
Vielen Dank. Es funktioniert jetzt wie erwartet. Jetzt verstehe ich den Unterschied zwischen dem Injizieren der Anbieter auf Bootstrap-Ebene und auf Komponentenebene.
Asubanovsky
Dieser Beitrag wurde ernsthaft in einen Zustand geändert, in dem ich ihn nicht mehr verstehe. Warum unterscheidet sich ein Aufruf des Dienstes .getNavChangeEmitter (), der den EventEmitter zurückgibt, von einem direkten Abonnieren von navChange?
Per Hornshøj-Schierbeck
@ PerHornshøj-Schierbeck, ich habe die Methode getNavChangeEmitter () verwendet, um die Tatsache zu verbergen, dass ein EventEmitter innerhalb des Dienstes verwendet wurde. Benutzer der Methode können davon ausgehen, dass das zurückgegebene Objekt über eine subscribe()Methode verfügt. Mithilfe einer Methode können wir den EventEmitter leicht in einen Betreff oder ein Observable ändern, was viel besser ist, da wir jetzt gelernt haben, dass EventEmitter nur für Ausgabeeigenschaften verwendet werden sollten. Der richtige Weg ist die Verwendung eines Betreffs, eines BehaviorSubjects oder eines Observable, und wir abonnieren diese normalerweise direkt, daher benötigen wir keine getNavChangeEmitter () -Methode mehr.
Mark Rajcok
6

Mit Alpha 28 habe ich es geschafft, Ereignisemitter mithilfe der eventEmitter.toRx().subscribe(..)Methode programmgesteuert zu abonnieren . Da es nicht intuitiv ist, kann es sich möglicherweise in einer zukünftigen Version ändern.

Runeboy
quelle
1
Vielleicht ein einfaches Beispiel für Leute, die auf diesen Beitrag stoßen? Ich habe übrigens nicht herabgestimmt;)
Per Hornshøj-Schierbeck
2
Ich weiß nicht, warum es herabgestimmt wurde, es funktioniert auf jeden Fall. Einfach ausgedrückt, um einen Event-Emitter "Klick" zu abonnieren, verwenden Sie click.toRx().subscribe(your_callback_function)
Runeboy
Ich denke, es könnte herabgestimmt worden sein, weil es kein funktionierendes Beispiel gibt? Ich weiß, dass es eine positive Bewertung und vielleicht eine akzeptierte Antwort wert wäre, wenn es entweder einen Link zu einer Pluncker / Jsfiddle-Geige oder sogar einige Codezeilen hätte, um die Syntax besser anzuzeigen;)
Per Hornshøj-Schierbeck
Ich erhalte AUSNAHME: Referenzfehler: Klick ist nicht definiert @Runeboy Können Sie bitte einen Plnkr für Ihre Lösung angeben?
Benjamin McFerren
Sie müssen einen EventEmitter mit dem Namen click definieren, damit er funktioniert
Mazen Elkashef,