web-dev-qa-db-fra.com

Angular2, désabonnement à un événement dans ngOnDestroy

Dans mon application, j'ai quelques composants qui communiquent au moyen de EventService.

@Injectable()
export class EventService {
  public myEvent: EventEmitter<any> = new EventEmitter();
  constructor() {}
}

Ce service est injecté dans un EmitterComponent qui émet l'événement quand un bouton est cliqué

@Component({
  selector: 'emitter',
  template: `<button (click)="onClick()">Click me</button>`,
})
export class EmitterComponent {
  constructor(private eventService:EventService) {}
  onClick() {
    this.eventService.myEvent.emit();
  }
}

et dans un ReceiverComponent qui souscrit à l'événement et pour chaque événement reçu incrémente un compteur

@Component({
  selector: 'receiver',
  template: `Count: {{count}}`,
})
export class ReceiverComponent {
  public count = 0;
  constructor(private eventService:EventService) {
    this.eventService.myEvent.subscribe(() => this.count++;);
  }
}

L'application a plusieurs vues (dans cet exemple seulement deux): PageA et PageB. EmitterComponent et ReceiverComponent sont dans PageA. Chaque fois que je vais dans PageB et que je reviens dans PageA, un nouveau ReceiverComponent est créé et lorsque je clique sur le bouton dans EmitterComponent, la fonction de rappel d'événement de ReceiverComponent est exécuté plusieurs fois.

Pour éviter cela, je me désinscris ReceiverComponent de myEvent dans ngOnDestroy

ngOnDestroy() {
  this.eventService.myEvent.unsubscribe();
}

mais cela provoque l'exception suivante

EXCEPTION: Error during instantiation of ReceiverComponent!.
ORIGINAL EXCEPTION: Error: Cannot subscribe to a disposed Subject

Comment puis-je éviter cela? Comment se désinscrire correctement?

Pour une meilleure compréhension, j'ai créé ce plunker où vous pouvez voir l'erreur et quelques commentaires dans la console.

23
TizianoL

Vous obtenez un abonnement auprès de .subscribe(). Utilisez sa méthode unsubscribe() pour annuler l'abonnement.

@Component({
  selector: 'receiver',
  template: `Count: {{count}}`,
})
export class ReceiverComponent {
  public count = 0;
  private subscription;
  constructor(private eventService:EventService) {
    this.subscription = this.eventService.myEvent.subscribe(() => this.count++;);
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Voir également

33
Günter Zöchbauer

Je pense que vous devez annuler l'abonnement, comme décrit ci-dessous:

export class ReceiverComponent {
  public count = 0;
  private id;

  constructor(private eventService:EventService) {
    this.id = Date.now();
    console.log("ReceiverComponent constructor " + this.id);
    this.subscription = this.eventService.myEvent.subscribe(() => {
      console.log("count " + this.count + " (id ReceiverComponent instance: " + this.id + ")");
      this.count++;
    });
  }

  ngOnDestroy() {
    console.log("onDestroy of ReceiverComponent " + this.id)
    //this cause "Cannot subscribe to a disposed Subject."
    //this.eventService.myEvent.unsubscribe();
    this.subscription.unsubscribe();
  }
}

En fait, EventEmitters sont des observables partagés, c'est-à-dire des observables à chaud. Voici un lien qui pourrait vous intéresser: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md .

J'espère que cela vous aide, Thierry

2
Thierry Templier