Paul Cowan

Nomadic cattle rustler and inventor of the electric lasso

Emberjs - Simple Global Event Bus

Following on from my last post about Communication Between Components, somebody rightly pointed out in the comments that another way of doing this is to create a global event bus as a service and inject it into any components that need it.

I mentioned in my last post that the requirements are for the publisher and subscriber of events to know nothing about each other. A global event bus fits this bill. A global event bus, allows publish/subscribe communication between components without requiring the components to explicitly register with one another (and thus be aware of each other).

I think this will give us the quickest win for the stated requirements and we can quickly cobble together an Ember.Service that will act as an event bus using Ember.Evented:

event_bus.js
1
2
3
4
5
6
7
8
9
10
11
App.EventBus = Ember.Service.extend(Ember.Evented, {
  publish: function() {
    return this.trigger.apply(this, arguments);
  },
  subscribe: function() {
    return this.on.apply(this, arguments);
  },
  unsubscribe: function() {
    return this.off.apply(this, arguments);
  }
});

An initializer is created to inject the event bus as a singleton into anything that might use it:

initializer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
Ember.Application.initializer({
  name: 'load-services',
  initialize: function(container, application) {
    var eventBus = App.EventBus.create();

    application.register('event-bus:current', eventBus, {
      instantiate: false
    });

    application.inject('component', 'EventBus', 'event-bus:current');
    application.inject('controller', 'EventBus', 'event-bus:current');
  }
});

Now a parent component can pubish events using the injected EventBus

publish.js
1
2
3
4
5
6
7
App.XPeopleComponent = Ember.Component.extend( {
  actions: {
    callChildren: function() {
      this.EventBus.publish('parentCalling');
    }
  }
});

Child components can now subscribe and unsubscribe to these events without referencing the parent component:

subscribe.js
1
2
3
4
5
6
7
8
9
10
11
12
13
App.XPersonComponent = Ember.Component.extend({
  _initialize: Ember.on('init', function(){
    this.EventBus.subscribe('parentCalling', this, 'onParentCalling');
  }),

  onParentCalling: function() {
    alert('parent called for ' + this.get('person.name'));
  },

  _teardown: Ember.on('willDestroyElement', function(){
    this.get('EventBus').unsubscribe('parentCalling');
  })
});

I think this is the quickest path to market right now as it requires little effort. Subscribing to events based on a string literal is less than ideal but this fits my purpose of being outside the bounds of data down and actions up where no data has changed and the publisher and subscriber don’t know anything about each other.

Here is a jsbin that puts it all together.

I am very open to somebody pointing out a better way.

Comments