Делегирование: EventEmitter или Наблюдаемое в Угловом

Я пытаюсь реализовать что-то вроде шаблона делегирования в Angular. Когда пользователь нажимает на nav-item , я хотел бы вызвать функцию, которая затем испускает событие, которое, в свою очередь, должно обрабатываться каким-либо другим компонентом, слушающим событие.

Вот сценарий: у меня есть компонент Navigation :

 import {Component, Output, EventEmitter} from 'angular2/core'; @Component({ // other properties left out for brevity events : ['navchange'], template:`  ` }) export class Navigation { @Output() navchange: EventEmitter = new EventEmitter(); selectedNavItem(item: number) { console.log('selected nav item ' + item); this.navchange.emit(item) } } 

Вот компонент наблюдения:

 export class ObservingComponent { // How do I observe the event ? //  public selectedNavItem(item: number) { console.log('item index changed!'); } } 

Ключевой вопрос: как заставить наблюдательный компонент наблюдать за происходящим?

    Обновление 2016-06-27: вместо использования Observables используйте либо

    • a BehaviorSubject, как рекомендовано @Abdulrahman в комментарии, или
    • ReplaySubject, как рекомендовано @Jason Goemaat в комментарии

    Субъект – это Observable (поэтому мы можем subscribe() на него) и Observer (поэтому мы можем вызвать next() на нем, чтобы испустить новое значение). Мы используем эту функцию. Объект позволяет значениям быть многоадресными для многих наблюдателей. Мы не используем эту функцию (у нас есть только один наблюдатель).

    BehaviorSubject – это вариант Subject. Он имеет понятие «текущее значение». Мы используем это: всякий раз, когда мы создаем ObservingComponent, он автоматически получает текущее значение навигационного элемента из BehaviorSubject.

    Код ниже и плункер используют BehaviorSubject.

    ReplaySubject – еще один вариант Subject. Если вы хотите подождать до фактического создания значения, используйте ReplaySubject(1) . В то время как для BehaviorSubject требуется начальное значение (которое будет предоставлено немедленно), ReplaySubject этого не делает. ReplaySubject всегда будет предоставлять самое последнее значение, но поскольку он не имеет требуемого начального значения, служба может выполнить некоторую операцию async, прежде чем вернуть первое значение. Он будет немедленно срабатывать при последующих вызовах с самым последним значением. Если вам просто нужно одно значение, используйте first() в подписке. Вам не нужно отказываться от подписки, если вы используете first() .

     import {Injectable} from '@angular/core' import {BehaviorSubject} from 'rxjs/BehaviorSubject'; @Injectable() export class NavService { // Observable navItem source private _navItemSource = new BehaviorSubject(0); // Observable navItem stream navItem$ = this._navItemSource.asObservable(); // service command changeNav(number) { this._navItemSource.next(number); } } 
     import {Component} from '@angular/core'; import {NavService} from './nav.service'; import {Subscription} from 'rxjs/Subscription'; @Component({ selector: 'obs-comp', template: `obs component, item: {{item}}` }) export class ObservingComponent { item: number; subscription:Subscription; constructor(private _navService:NavService) {} ngOnInit() { this.subscription = this._navService.navItem$ .subscribe(item => this.item = item) } ngOnDestroy() { // prevent memory leak when component is destroyed this.subscription.unsubscribe(); } } 
     @Component({ selector: 'my-nav', template:`  ` }) export class Navigation { item = 1; constructor(private _navService:NavService) {} selectedNavItem(item: number) { console.log('selected nav item ' + item); this._navService.changeNav(item); } } 

    Plunker


    Оригинальный ответ, который использует Observable: (для этого требуется больше кода и логики, чем использование BehaviorSubject, поэтому я не рекомендую его, но это может быть поучительным)

    Итак, вот реализация, которая использует Observable вместо EventEmitter . В отличие от моей реализации navItem эта реализация также сохраняет navItem выбранный navItem в службе, так что, когда создается компонент наблюдения, он может получить текущее значение через API-вызов navItem() , а затем получать уведомления об изменениях через navChange$ Observable ,

     import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/share'; import {Observer} from 'rxjs/Observer'; export class NavService { private _navItem = 0; navChange$: Observable; private _observer: Observer; constructor() { this.navChange$ = new Observable(observer => this._observer = observer).share(); // share() allows multiple subscribers } changeNav(number) { this._navItem = number; this._observer.next(number); } navItem() { return this._navItem; } } @Component({ selector: 'obs-comp', template: `obs component, item: {{item}}` }) export class ObservingComponent { item: number; subscription: any; constructor(private _navService:NavService) {} ngOnInit() { this.item = this._navService.navItem(); this.subscription = this._navService.navChange$.subscribe( item => this.selectedNavItem(item)); } selectedNavItem(item: number) { this.item = item; } ngOnDestroy() { this.subscription.unsubscribe(); } } @Component({ selector: 'my-nav', template:`   `, }) export class Navigation { item:number; constructor(private _navService:NavService) {} selectedNavItem(item: number) { console.log('selected nav item ' + item); this._navService.changeNav(item); } } 

    Plunker


    См. Также пример Cookbook Component Interaction , в котором в дополнение к наблюдаемым используется объект Subject . Хотя примером является «связь между родителями и детьми», тот же метод применим для несвязанных компонентов.

    Последние новости: я добавил еще один ответ, который использует Observable, а не EventEmitter. Я рекомендую ответить на этот вопрос. И на самом деле использование EventEmitter в сервисе – это плохая практика .


    Оригинальный ответ: (не делайте этого)

    Поместите EventEmitter в службу, которая позволяет ObservingComponent напрямую подписаться (и отписаться) на событие :

     import {EventEmitter} from 'angular2/core'; export class NavService { navchange: EventEmitter = new EventEmitter(); constructor() {} emit(number) { this.navchange.emit(number); } subscribe(component, callback) { // set 'this' to component when callback is called return this.navchange.subscribe(data => call.callback(component, data)); } } @Component({ selector: 'obs-comp', template: 'obs component, index: {{index}}' }) export class ObservingComponent { item: number; subscription: any; constructor(private navService:NavService) { this.subscription = this.navService.subscribe(this, this.selectedNavItem); } selectedNavItem(item: number) { console.log('item index changed!', item); this.item = item; } ngOnDestroy() { this.subscription.unsubscribe(); } } @Component({ selector: 'my-nav', template:`  `, }) export class Navigation { constructor(private navService:NavService) {} selectedNavItem(item: number) { console.log('selected nav item ' + item); this.navService.emit(item); } } 

    Если вы попробуете Plunker , есть несколько вещей, которые мне не нравятся в этом подходе:

    • ObservingComponent должен отказаться от подписки, когда он будет уничтожен
    • мы должны передать компонент для subscribe() чтобы правильное this задано при вызове обратного вызова

    Обновление: альтернатива, которая решает navchange пулю, заключается в том, чтобы ObservingComponent напрямую подписался на свойство navchange :

     constructor(private navService:NavService) { this.subscription = this.navService.navchange.subscribe(data => this.selectedNavItem(data)); } 

    Если мы подписываемся напрямую, нам не нужен метод subscribe() в NavService.

    Чтобы сделать NavService немного инкапсулированным, вы можете добавить метод getNavChangeEmitter() и использовать его:

     getNavChangeEmitter() { return this.navchange; } // in NavService constructor(private navService:NavService) { // in ObservingComponent this.subscription = this.navService.getNavChangeEmitter().subscribe(data => this.selectedNavItem(data)); } 

    Если вы хотите следовать более реактивному ориентированному стилю программирования, то определенно понятие «все – stream» входит в картину и, следовательно, использует Observables для работы с этими streamами как можно чаще.

    вы можете использовать BehaviourSubject, как описано выше, или есть еще один способ:

    вы можете обработать EventEmitter следующим образом: сначала добавьте селектор

     import {Component, Output, EventEmitter} from 'angular2/core'; @Component({ // other properties left out for brevity selector: 'app-nav-component', //declaring selector template:`  ` }) export class Navigation { @Output() navchange: EventEmitter = new EventEmitter(); selectedNavItem(item: number) { console.log('selected nav item ' + item); this.navchange.emit(item) } } 

    Теперь вы можете обработать это событие, например , предположим, что observer.component.html – это представление компонента Observer

      

    затем в ObservingComponent.ts

     export class ObservingComponent { //method to recieve the value from nav component public recieveIdFromNav(id: number) { console.log('here is the id sent from nav component ', id); } } 

    Вам необходимо использовать компонент «Навигация» в шаблоне ObservingComponent (не забывайте добавлять селектор в компонент «Навигация» .. «навигационная компонента для ex»)

      

    И реализовать onNavGhange () в ObservingComponent

     onNavGhange(event) { console.log(event); } 

    Последнее, что вам не нужен атрибут событий в @Componennt

     events : ['navchange'], 

    Я нашел другое решение для этого случая, не используя Reactivex ни одну из служб. Мне действительно нравится API-интерфейс rxjx, но я думаю, что он лучше всего подходит для решения асинхронной и / или сложной функции. Используя его таким образом, он довольно превзошел меня.

    Я думаю, что вы ищете для трансляции. Только то. И я нашел это решение:

       // This component bellow wants to know when a tab is selected // broadcast here is a property of app component   @Component class App { broadcast: EventEmitter; constructor() { this.broadcast = new EventEmitter(); } onSelectedTab(tab) { this.broadcast.emit(tab) } } @Component class AppInterestedComponent implements OnInit { broadcast: EventEmitter(); doSomethingWhenTab(tab){ ... } ngOnInit() { this.broadcast.subscribe((tab) => this.doSomethingWhenTab(tab)) } } 

    Это полный рабочий пример: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE

    Interesting Posts

    Использование Wget для рекурсивного сканирования сайта и загрузки изображений

    Переключение между изображениями Android Navigation Drawer и Up caret при использовании fragmentов

    Как я могу округлить значение float до двух постсимвольных позиций?

    Мощность модуля больших чисел

    Excel условное форматирование даты до выходных / праздников

    Как настроить фокусировку на JTextField?

    Visual Studio открывает браузер по умолчанию вместо Internet Explorer

    Как я могу генерировать поистине (а не псевдо) случайные числа с помощью C #?

    Выделить выбранную строку ListView

    Как проверить блокировку файлов?

    Шаблон управления существующими элементами управления в WPF

    Как мне перейти на подключенный сетевой диск в командной строке?

    Как сериализовать / десериализовать простые classы в XML и обратно

    Несколько отправки в C ++

    Как создать пользовательские методы для использования в выражениях выражения выражения весны безопасности

    Давайте будем гением компьютера.