I am trying to implement something like a delegation pattern in Angular.
When the user clicks on a nav-item
, I would like to call a function which then emits an event which should in turn be handled by some other component listening for the event.
Here is the scenario: I have a Navigation
component:
import {Component, Output, EventEmitter} from 'angular2/core';
@Component({
// other properties left out for brevity
events : ['navchange'],
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
@Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
Here is the observing component:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
The key question is, how do I make the observing component observe the event in question ?
I found out another solution for this case without using Reactivex neither services. I actually love the rxjx API however I think it goes best when resolving an async and/or complex function. Using It in that way, Its pretty exceeded to me.
What I think you are looking for is for a broadcast. Just that. And I found out this solution:
This is a full working example: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE
you can use BehaviourSubject as described above or there is one more way:
you can handle EventEmitter like this: first add a selector
Now you can handle this event like let us suppose observer.component.html is the view of Observer component
then in the ObservingComponent.ts
Update 2016-06-27: instead of using Observables, use either
A Subject is both an Observable (so we can
subscribe()
to it) and an Observer (so we can callnext()
on it to emit a new value). We exploit this feature. A Subject allows values to be multicast to many Observers. We don't exploit this feature (we only have one Observer).BehaviorSubject is a variant of Subject. It has the notion of "the current value". We exploit this: whenever we create an ObservingComponent, it gets the current navigation item value from the BehaviorSubject automatically.
The code below and the plunker use BehaviorSubject.
ReplaySubject is another variant of Subject. If you want to wait until a value is actually produced, use
ReplaySubject(1)
. Whereas a BehaviorSubject requires an initial value (which will be provided immediately), ReplaySubject does not. ReplaySubject will always provide the most recent value, but since it does not have a required initial value, the service can do some async operation before returning it's first value. It will still fire immediately on subsequent calls with the most recent value. If you just want one value, usefirst()
on the subscription. You do not have to unsubscribe if you usefirst()
.Plunker
Original answer that uses an Observable: (it requires more code and logic than using a BehaviorSubject, so I don't recommend it, but it may be instructive)
So, here's an implementation that uses an Observable instead of an EventEmitter. Unlike my EventEmitter implementation, this implementation also stores the currently selected
navItem
in the service, so that when an observing component is created, it can retrieve the current value via API callnavItem()
, and then be notified of changes via thenavChange$
Observable.Plunker
See also the Component Interaction Cookbook example, which uses a
Subject
in addition to observables. Although the example is "parent and children communication," the same technique is applicable for unrelated components.You can use either:
BehaviorSubject is a type of subject, a subject is a special type of observable which can act as observable and observer you can subscribe to messages like any other observable and upon subscription, it returns the last value of the subject emitted by the source observable:
Advantage: No Relationship such as parent-child relationship required to pass data between components.
NAV SERVICE
NAVIGATION COMPONENT
OBSERVING COMPONENT
Second Approach is
Event Delegation in upward direction child -> parent
e.g Answered given by @Ashish Sharma.
Breaking news: I've added another answer that uses an Observable rather than an EventEmitter. I recommend that answer over this one. And actually, using an EventEmitter in a service is bad practice.
Original answer: (don't do this)
Put the EventEmitter into a service, which allows the ObservingComponent to directly subscribe (and unsubscribe) to the event:If you try the Plunker, there are a few things I don't like about this approach:
subscribe()
so that the properthis
is set when the callback is calledUpdate: An alternative that solves the 2nd bullet is to have the ObservingComponent directly subscribe to the
navchange
EventEmitter property:If we subscribe directly, then we wouldn't need the
subscribe()
method on the NavService.To make the NavService slightly more encapsulated, you could add a
getNavChangeEmitter()
method and use that:If one wants to follow a more Reactive oriented style of programming, then definitely the concept of "Everything is a stream" comes into picture and hence, use Observables to deal with these streams as often as possible.