-
-
Save joelhooks/bc5a0065ee661d2588c9 to your computer and use it in GitHub Desktop.
This is a global event bus for AngularJS applications. It abstracts `$rootScope` and uses it as the bus. With the `DispatchingController` mixin, controllers and add events easily and not be concerned with memory-leaks (or worse, catastrophic event handling by "dead" controllers). It also adds in some helper methods for "off-scope" services that …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('event-dispatcher', []) | |
/** | |
* This factory provides a constructor to mixin with an | |
* AngularJS controller constructor. It provides some | |
* syntax sugar for making memory-leak safe eventing. | |
* | |
* We define the constructor as a factory so that we can | |
* inject the constructor. This is useful for testing so | |
* we aren't hard-instantiating a namespaced global function | |
* and can substitute it when needed. | |
*/ | |
.factory('DispatchingController', function ($rootScope) { | |
function DispatchingController($scope) { | |
var delisteners = []; | |
if (!$scope) { | |
throw new Error("A DispatchingController must have $scope to function. It wasn't found.") | |
} | |
this.dispatch = $rootScope.$emit.bind($rootScope); | |
this.listen = function () { | |
var args = Array.prototype.slice.call(arguments), | |
deListenFunc = $rootScope.$on.apply($rootScope, args); | |
delisteners.push(deListenFunc); | |
return deListenFunc; | |
}; | |
// this is the "why" of this mixin. We want to be able to | |
// listen for events on the rootScope bus, but we also | |
// want to make sure we don't have memory leaks. | |
// Instead of managing all of that in the controller, we | |
// can use this mixin and get very clean/safe results. | |
$scope.$on('$destroy', function () { | |
delisteners.forEach(function (deListenFunc) { | |
deListenFunc.call(); | |
}) | |
}) | |
} | |
return DispatchingController; | |
}) | |
/** | |
* This factory provides a `dispatch` function as | |
* an injectable. The dispatch function is a wrapper | |
* around $rootScope.$emit, but allows us to inject | |
* just the single piece of functionality we want | |
* into ANY service/factory/etc and facilitate event | |
* dispatching. | |
* | |
* We want to discourage services (models) from LISTENING | |
* for events, but they need to dispatch events to notify | |
* other actors when changes occur. | |
*/ | |
.factory('dispatch', function (Dispatcher) { | |
var dispatcher = new Dispatcher(); | |
return dispatcher.dispatch; | |
}) | |
/** | |
* This factory creates a Dispatcher constructor. This can | |
* be used as a method for mixing in dispatching capabilities. | |
* | |
* Generally we'll just use the dispatch function above. | |
*/ | |
.factory('Dispatcher', function($rootScope) { | |
function Dispatcher() {} | |
Dispatcher.prototype.dispatch = function () { | |
var args = Array.prototype.slice.call(arguments); | |
$rootScope.$emit.apply($rootScope, args); | |
}; | |
return Dispatcher; | |
}) | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this might be sinful, but it is pretty and I like it ;) | |
Object.defineProperty(Object.prototype, 'mixin', { | |
value: function (receiver, giver) { | |
for (var i in giver) { | |
if (!this.hasOwnProperty(i)) { | |
Object.defineProperty(receiver, i, { | |
value: giver[i], | |
enumerable: true | |
}); | |
} | |
} | |
}, | |
enumerable: false | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This is how you'd use the dispatcher | |
*/ | |
.controller('myController', function ($scope, DispatchingController) { | |
var time = new Date(); | |
// `this` scope can be an issue here. It might be | |
// required to assign `var myController = this` | |
// in some cases. | |
// | |
// this could be `angular.extend` vs the Object.mixin ;) | |
this.mixin(this, new DispatchingController($scope)); | |
this.listen('hi', function (event, msg) { | |
console.log('event handled:', time, msg, event); | |
}); | |
this.dispatch('hi', 'from controller') | |
}) | |
.service('myService', function(dispatch) { | |
dispatch('hi', 'from service'); | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment