Як підписатися на подію на сервісі в 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

EDIT: Модифікований мій "робочий приклад" фактично працював, тому можна зосередити увагу на неробочій частині;)

Приклад коду: http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv


1
Це може бути більше проблемою дизайну, оскільки служба не повинна випромінювати події від імені компонента, оскільки це щільно з'єднує службу з компонентом. наприклад, що робити, якщо є кілька примірників компонента, чи всі вони повинні випромінювати події в цьому випадку?
Кутовий університет

@jhadesdev Я тебе читав. Я переробив рішення, щоб сервісу більше не потрібно видавати результат. Я все ще думаю, що деякі проекти могли б використати можливість підняти події - залежно від того, що це за "послуга" ...
Per Hornshøj-Schierbeck

Одним із способів може бути компонент створити випромінювач події замість служби, а потім передати емітер події як аргумент до служби
Angular University

Відповіді:


135

Оновлення : Я знайшов кращий / правильний спосіб вирішити цю проблему за допомогою BehaviorSubject або спостережуваного, а не EventEmitter. Дивіться цю відповідь: https://stackoverflow.com/a/35568924/215945

Також у кутових документах тепер є приклад кулінарної книги, в якому використовується тема .


Оригінальна / застаріла / неправильна відповідь: знову ж таки, не використовуйте EventEmitter у сервісі . Це анти-модель.

Використання beta.1 ... NavService містить EventEmiter. Навігація компонентів випромінює події через сервіс, а компонент 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;
  }
}

компоненти.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


Я дотримувався цього, але це працює лише для самого класу. Він не працює для іншого класу (з іншого файлу). Здається, проблема в екземплярі класу обслуговування. Чи варто зробити клас обслуговування статичним?
Асубановський

5
@asubanovsky, у планкера я ввожу лише NavService на рівні завантаження (тобто кореневий компонент), тому для всього додатка має бути лише один екземпляр служби. Ви ін'єктуєте його деінде providers: [NavService]? Якщо так, ви отримаєте кілька примірників служби.
Марк Райкок

Велике спасибі. Зараз це працює як моє сподівання. Тепер я розумію різницю між введенням постачальників послуг на рівні завантаження та на рівні компонентів.
асубановський

Ця посада була серйозно змінена до стану, коли я її більше не отримую. Чому викликає mehtod на сервісі .getNavChangeEmitter (), який повертає EventEmitter, відмінний від прямої підписки на navChange?
Per Hornshøj-Schierbeck

@ PerHornshøj-Schierbeck, я використовував метод getNavChangeEmitter (), щоб приховати факт, що в службі використовується EventEmitter. Користувачі методу можуть припустити, що повернутий об'єкт має subscribe()метод. Використовуючи метод, ми можемо легко змінити EventEmitter на Subject або спостережуваний, що набагато краще, оскільки ми зараз дізналися, що EventEmitters слід використовувати лише для властивостей Output. Правильний спосіб полягає у використанні Subject, BehaviorSubject або Observable, і ми зазвичай підписуємося на них, отже, нам більше не потрібен метод getNavChangeEmitter ().
Марк Райкок

6

Використовуючи альфа-28, я здійснив програмну підписку на випромінювачі подій eventEmitter.toRx().subscribe(..)методом. Оскільки це не інтуїтивно, це може змінитися в майбутньому випуску.


1
можливо, наведіть простий приклад для використання людьми, якщо вони натрапляють на цю посаду? Це не я, зводячи доріг;)
Per Hornshøj-Schierbeck

2
Не знаю, чому це було знято, але це, безумовно, працює. Простіше кажучи, щоб підписатися на емітер події "натисніть", використовуйте click.toRx().subscribe(your_callback_function)
Runeboy

Я думаю, це могло бути знято з огляду на те, що йому не вистачає робочого прикладу? Я знаю , що це буде коштувати upvote і , можливо , загальноприйнятий відповідь , якщо він мав або посилання на pluncker / jsfiddle скрипкою або навіть кілька рядків коду , щоб краще відобразити синтаксис;)
Per Hornshøj-Schierbeck

Я отримую ВИЗНАЧЕННЯ: ReferenceError: натисніть не визначено @Runeboy Чи можете ви надати plnkr для свого рішення?
Бенджамін МакФеррен

Вам потрібно визначити EventEmitter, який називається клацанням, щоб він працював
Mazen Elkashef
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.