Angular Events
I’ve been trying to find an elegant way of dealing with events in AngularJS recently. If you’re not farmiliar with Angular, that’s ok, this is a pretty common pattern.
Here I have a controller that registers an event listener:
function MyController($rootScope) {
$rootScope.$on('event1', () => {
console.log('event 1 occured');
});
}
There’s an issue with this code. It doesn’t unbind the listener when the controller (or its scope) is destroyed. Let’s take care of this.
function MyController($scope, $rootScope) {
let unbindEvent1 = $rootScope.$on('event1', () => {
console.log('event 1 occured');
});
$scope.$on('$destroy', unbindEvent1);
}
This is ok, but gets unwieldy when you have multiple listeners.
function MyController($scope, $rootScope) {
let unbindThisHappened = $rootScope.$on('thisHappened', () => {
console.log('this happened');
});
let unbindThatHappened = $rootScope.$on('thatHappened', () => {
console.log('that happened');
});
let unbindErrorHappened = $rootScope.$on('errorHappened', () => {
console.log('error happened');
});
$scope.$on('$destroy', () => {
unbindThisHappened();
unbindThatHappened();
unbindErrorHappened();
});
}
A better way would be to have something called a ListenerGroup
. Here’s how it would work.
function MyController($scope, $rootScope) {
let listeners = ListenerGroup.for($rootScope);
listeners.$on('thisHappened', () => console.log('this'));
listeners.$on('thatHappened', () => console.log('that'));
listeners.$on('errorHappened', () => console.log('error'));
$scope.$on('$destroy', () => listeners.unbind());
}
If the ListenerGroup
was made to be angular aware, you could even take it a step further.
I’m not too sure about this, because it’s not apparent what link
does and it doesn’t really save that much typing.
function MyController($scope, $rootScope) {
let listeners = ListenerGroup.for($rootScope);
listeners.$on('thisHappened', () => console.log('this'));
listeners.$on('thatHappened', () => console.log('that'));
listeners.$on('errorHappened', () => console.log('error'));
listeners.link($scope);
}
Implementing ListenerGroup
is pretty simple.
class ListenerGroup {
constructor($scope) {
this._unbinds = [];
this._scope = $scope;
}
$on(event, listener) {
let unbind = this._scope.$on(event, listener);
this._unbinds.push(unbind);
}
unbind() {
for (let unbind of this._unbinds) {
unbind();
}
}
link($scope) {
$scope.$on('$destroy', () => this.unbind());
}
static for($scope) {
return new ListenerGroup($scope);
}
}