Skip to content

Instantly share code, notes, and snippets.

@naholyr
Last active December 14, 2015 11:48
Show Gist options
  • Save naholyr/5081206 to your computer and use it in GitHub Desktop.
Save naholyr/5081206 to your computer and use it in GitHub Desktop.
A dumb event emitter service for Angular, primary usage is to allow transparent communication between controllers → http://jsfiddle.net/NuCjC/1/

Sample usage on JSFiddle

Disclaimer

This may be a dumb solution, you'd better use $rootScope.$on(event, handler) and $rootScope.$emit(event, args…) and not use a third-party service.

My solution will bring you:

  • support for off() if you really want to clean up your event handlers
  • maybe a conceptually better solution than using scopes, as with a service you make yourself independant from your view

“In real life” I'd advise using $rootScope.$on or even parentScope.$watch if you have a common parent scope above your controllers.

// Usage:
// Depend on "EventEmitterService"
// Then add "$ev" in your controller dependencies
//
// API is very simple:
// * $ev.on('event:name', function (params…) { … })
// * $ev.emit('event:name', params…)
// * $ev.off('event:name' [, handler])
//
// jQuery is required if you want to pass parameters when emitting
// events. And you might want to :)
// Angular's jqLite will emit event, but won't include data.
//
// Yes, the jQuery DOM Event is lost in the process, this is NOT
// what we want to manipulate here, this service is NOT intended
// to be used for DOM events.
angular
.module('EventEmitterService', [])
.factory('$ev', function () {
// Create a DOM element to handle events transport for us
// Note: jQuery required to emit() additional data along with event name
var $e = (typeof jQuery !== 'undefined' ? jQuery : angular.element)('<div/>');
// Cache generated handler so "off" can work
var handlers = {};
// Get or generate a handler and keep reference to original function
var getHandler = function (eventType, original, generate) {
if (!handlers[eventType]) {
handlers[eventType] = [];
}
var evHandlers = handlers[eventType];
for (var i=0; i<evHandlers.length; i++) {
if (evHandlers[i][0] === original) return evHandlers[i][1];
}
// not found
if (generate) {
var handler = function () {
// Call handler without first parameter (the jQuery event)
original.apply(this, Array.prototype.slice.call(arguments, 1));
};
handler._index = evHandlers.push([original, handler]);
return handler;
}
};
// Remove kept references (free memory along with off())
var removeHandlers = function (eventType, handler) {
if (!original) {
// Remove all handlers for this type
delete handlers[eventType];
} else if (handlers[eventType] && handler && !isNaN(handler._index)) {
// Remove only the bound handler
handlers[eventType].splice(handler._index);
if (!handlers[eventType].length) delete handlers[eventType];
}
};
// Public API:
return {
// on(eventType, handler)
"on": function (eventType, handler) {
$e.bind(eventType, getHandler(eventType, handler, true));
return this;
},
// emit(eventType [, params…])
"emit": function (eventType) {
var params = Array.prototype.slice.call(arguments, 1);
$e.triggerHandler(eventType, params);
return this;
},
// off(eventType[, handler])
"off": function (eventType, handler) {
if (handler) {
var boundHandler = getHandler(handler, false);
removeHandlers(eventType, boundHandler);
$e.unbind(eventType, boundHandler);
} else {
$e.unbind(eventType);
}
return this;
}
};
});
@leon
Copy link

leon commented Mar 4, 2013

Using jQuery, maybe this could be an alternative.
https://gist.github.com/leon/5082077

@Nate-Wilkins
Copy link

Honestly don't think this is dumb. Broadcasting all events on $rootScope (any $scope) sounds dumb especially if it's not relevant to those $scopes at all. I like this better than treating $scope.emit/$scope.on as the global container for all my events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment