ponyfoo.com

Event Emitter: Obey and Report

The event emitter pattern was popularized by Node, and is made available in the browser by libraries such as EventEmitter2. In practice, I haven’t seen a lot of use of event emitters in client-side applications, but lately I’ve been applying it more and more in my code. Here, I’ll introduce a modular approach to client-side development I didn’t invent, but which I decided to name, anyways.

Improve this article
Nicolás Bevacqua
| 6 minute read | 0

Obey and Report

Recently, I came up with this idea where I laid out a library that doesn’t necessarily depend on Angular, but which plays very nicely with it. The pattern basically dictates that the components can only change their state through commands, which can come from either its UI directly, or be programatically invoked. This allows me to provide a default UI for the component, but is flexible enough to let an Angular application take over and replace that UI layer with its own, which then simply dictates the library to obey the commands it’s sent. Reporting happens whenever a public state property is updated, and the UI could use said property to update its representation of the component’s state.

obey-report
obey-report

One way to make this communication pattern effective is using an event emitter (some day Object.observe will be a realistic alternative), and emit events whenever our state changes. In code, we can make our library an event emitter like below. We’ll keep state in a separate private variable so that it can’t be accessed from outside of our library, making sure we’re masters of the universe when it comes to one of our instance’s state.

// always use closures, keep it to yourself
(function (window, EventEmitter2) {
  // see: http://stackoverflow.com/q/8651415/389745
  'use strict';

  // we'll keep track of state in a private variable only we can access
  var state = {};

  // we'll use this to give each instance a unique id
  var lastId = -1;

  // this is our Module's constructor
  function Module () {
    var self = this;

    // assign a new unique id
    self._id = ++lastId;
    
    // initialize our state with some defaults
    state[self._id] = {
      x: 0, y: 0
    };
    
    // invoke the EventEmitter2 constructor
    EventEmitter2.call(self, {
      wildcard: true
    });
  }

  // inherit from EE2 prototype, 'subclassing' it
  Module.prototype = Object.create(EventEmitter2.prototype);
  Module.prototype.constructor = Module;

  // expose our Module as some Thing the global object has access to.
  window.Thing = Module;
})(window, EventEmitter2);

Then, applying this pattern is simply a matter of exposing commands implementors can interact with (our public interface), and emitting changes to our properties, avoiding to expose these directly. We will expose commands, such as move, where users of our library can change the state of their components in a clearly defined way. They won’t be able to access this state, however, except when we choose to expose it, so they need to be paying attention to our emitted events.

// the move command
Module.prototype.move = function (x, y) {
  // get the old X and Y states, add some distance to them
  set(this, 'x', get(this, 'x') + x);
  set(this, 'y', get(this, 'y') + y);
};

// internal state setter
function set (instance, key, value) {
  state[instance._id][key] = value;

  // emit events when properties change
  instance.emit(['report', key], value);  
}

// internal state getter
function get (instance, key) {
  return state[instance._id][key];
}

Say we were using keymaster to handle keyboard input, our module could then consume input like below. Note how we don’t really even know what we’re doing, we are just telling the component:

Hey, Thing!, Obey this command, .move!

The code would look something like this.

// get a Thing instance, obviously we'll always want the same one
var component = new Thing();

var map = {
  left: -1,
  right: 1,
  up: -1,
  down: 1
};

// on left or right, move in the X axis
key('left, right', function (e, h) {
  // tell the component to move
  component.move( map[h.shortcut], 0 );
});

// on up or down, move in the Y axis
key('up, down', function (e, h) {
  // tell the component to move
  component.move( 0, map[h.shortcut] );
});

Lastly, updating our UI would also be completely decloupled from the rest of the code. Whenever the state changes, we update the position of the UI representation for our Thing instance. We don’t care how it changed, we only care that it did.

// get the element that represents our state in the UI
var element = document.getElementById('square');

// whenever we get a report on X...
component.on('report.x', function (value) {
  // update our left position
  element.style.left = value + 'px';
});

// whenever we get a report on Y...
component.on('report.y', function (value) {
  // update our top position
  element.style.top = value + 'px';
});

// initialize at X=50, Y=50
component.move(50, 50);

A working example can be found clicking on the image below.

thing.png
thing.png

This kind of pattern might be most useful when your components present complex interactions between their different properties. In those cases, it might be even better to use Angular.js. Although sometimes a combination of both might also prove to be useful. Of course, the hiding of information is entirely optional, and it might make sense not to hide anything but instead expose the properties in our components directly, however the event emitter pattern still makes sense to “report” when these events take place.

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments