Как подписаться на событие сервиса в Angular2?

112

Я знаю, как вызвать событие с помощью EventEmitter. Я также могу прикрепить метод, который будет вызываться, если у меня есть такой компонент:

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

Когда у меня есть такой компонент, все отлично работает. Я перенес некоторую логику в службу, и мне нужно вызвать событие внутри службы. Я сделал вот что:

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

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

У меня есть компонент, которому необходимо обновить какое-то значение на основе этого события, но я не могу заставить его работать. Я пробовал вот что:

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

Как заставить MyComponent подписаться на событие, когда вызывающая его служба не является компонентом?

Я использую 2.0.0-alpha.28

РЕДАКТИРОВАТЬ: изменен мой «рабочий пример», чтобы он действительно работал, поэтому можно сосредоточить внимание на неработающей части;)

Пример кода: http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv

Пер Хорнсхой-Ширбек
источник
1
Это может быть больше проблема дизайна, поскольку служба не должна генерировать события от имени компонента, поскольку это тесно связывает службу с компонентом. например, что, если существует несколько экземпляров компонента, все ли они должны в этом случае генерировать события?
Angular University
@jhadesdev Я читал тебя. Я перепроектировал решение, поэтому сервису больше не нужно выдавать результат. Я все еще думаю, что некоторые дизайны выиграют от возможности поднимать события - в зависимости от того, что это за «услуга» ...
Пер Хорншой-Ширбек
один из способов - заставить компонент создать эмиттер событий вместо службы, а затем передать эмиттер событий в качестве аргумента службе
Angular University

Ответы:

135

Обновление : я нашел лучший / правильный способ решить эту проблему, используя BehaviorSubject или Observable, а не EventEmitter. См. Этот ответ: https://stackoverflow.com/a/35568924/215945

Кроме того, в документации Angular теперь есть пример поваренной книги, в которой используется тема .


Оригинальный / устаревший / неправильный ответ: опять же, не используйте EventEmitter в сервисе . Это антипаттерн.

Используя beta.1 ... NavService содержит EventEmiter. Component Navigation испускает события через службу, а компонент ObservingComponent подписывается на события.

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

components.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

Марк Райкок
источник
Я следил за этим, но это работает только для самого класса. Это не работает для другого класса (из другого файла). Похоже, проблема в экземпляре класса обслуживания. Есть ли способ сделать класс обслуживания статическим?
asubanovsky
5
@asubanovsky, в плункер я внедряю NavService только на уровне начальной загрузки (т.е. корневого компонента), поэтому должен быть только один экземпляр службы для всего приложения. Вы вводите его в другое место providers: [NavService]? Если это так, вы получите несколько экземпляров службы.
Марк Райкок,
Большое спасибо. Теперь он работает, как я ожидал. Теперь я понимаю разницу между внедрением провайдеров на уровне начальной загрузки и на уровне компонентов.
asubanovsky
Этот пост был серьезно изменен до состояния, когда я его больше не понимаю. Почему вызов метода в службе .getNavChangeEmitter (), который возвращает EventEmitter, отличается от прямой подписки на navChange?
Пер Хорнсхой-Ширбек
@ PerHornshøj-Schierbeck, я использовал метод getNavChangeEmitter (), чтобы скрыть тот факт, что внутри службы использовался EventEmitter. Пользователи метода могут предположить, что у возвращаемого объекта есть subscribe()метод. Используя метод, мы можем легко изменить EventEmitter на Subject или Observable, что намного лучше, поскольку теперь мы узнали, что EventEmitter следует использовать только для свойств вывода. Правильный способ - использовать Subject, BehaviorSubject или Observable, и мы обычно подписываемся на них напрямую, поэтому нам больше не нужен метод getNavChangeEmitter ().
Mark Rajcok
6

Используя alpha 28, я выполнил программную подписку на эмиттеры событий посредством eventEmitter.toRx().subscribe(..)метода. Поскольку это не интуитивно понятно, возможно, оно изменится в будущем выпуске.

Рунный мальчик
источник
1
Может быть, предоставить простой пример для использования людьми, если они наткнутся на этот пост? Кстати, это не я голосовал против;)
Пер Хорншой-Ширбек
2
Не знаю, почему это было отклонено, это определенно работает. Проще говоря, чтобы подписаться на эмиттер событий «щелчок», используйте click.toRx().subscribe(your_callback_function)
Runeboy
Я думаю, он мог быть отклонен, потому что ему не хватает рабочего примера? Я знаю, что это стоило бы одобрения и, возможно, принятого ответа, если бы у него была либо ссылка на скрипт pluncker / jsfiddle, либо даже несколько строк кода для лучшего отображения синтаксиса;)
Пер Хорнсхой-Ширбек
Я получаю ИСКЛЮЧЕНИЕ: ReferenceError: click не определен @Runeboy Не могли бы вы предоставить plnkr для вашего решения?
Бенджамин МакФеррен
Вам нужно определить EventEmitter, называемый щелчком, чтобы он работал
Мазен Элькашеф,