patternjavascriptMinor
Angular wrapper for SignalR event aggregator
Viewed 0 times
angularaggregatorwrapperforsignalrevent
Problem
I created a little wrapper for one of my JavaScript libraries to enable Angular functionality. Are there any pitfalls I should be wary of with my code?
More info on the code itself here.
It's used like this:
Other scenarios it supports are
Generic events, (all events are server events proxied to javascript through a dynamic Owin javascript)
Constraints, in this case listen to all events with id 5
It wraps an event-driven library, and when an event is triggered, I use
angular.module("signalR.eventAggregator", [])
.run([
"$rootScope", function($rootScope) {
function createScope(scope) {
scope.$on('$destroy', function() {
signalR.eventAggregator.unsubscribe(scope);
});
return {
subscribe: function(type, handler, constraint) {
signalR.eventAggregator.subscribe(type, function(e) {
handler(e);
if (scope.$phase == null) {
scope.$digest();
}
}, scope, constraint);
},
publish: function(event) {
signalR.eventAggregator.publish(event);
}
}
}
$rootScope.eventAggregator = function() {
return this.__eventAggregator = this.__eventAggregator || createScope(this);
};
}
]);More info on the code itself here.
It's used like this:
$scope.eventAggregator().subscribe(MyApp.MyEvent, onEvent);Other scenarios it supports are
Generic events, (all events are server events proxied to javascript through a dynamic Owin javascript)
$scope.eventAggregator().subscribe(MyApp.MyGenericEvent.of("System.String"), onEvent);Constraints, in this case listen to all events with id 5
$scope.eventAggregator().subscribe(MyApp.MyEvent, onEvent, { id: 5 });It wraps an event-driven library, and when an event is triggered, I use
digest to update the view, and it's this part of the code that I wonder about.handler(e); could return a promise that I currently ignore. I tested the ng-click directive and it also ignores any promise returned from the click handler. So at least it's consistent with the existing event handlers in Angular.Solution
Hmm... interesting use of
As an alternative, you could wrap your event relay in a factory.
-
As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is missing on initialization, unlike
-
Factories are singletons. You don't have to worry about checking and using the existing or create a new one.
-
You're not polluting the
Additionally, you might also want to put your event names in a constant. That way, you have a global lookup of event names, and you won't be hardcoding string literals all over the app.
Here's an example of how it would look like as a factory (using implicit dependency injection syntax for brevity).
Now I did mention that angular has a built-in pub-sub system. We can just hook on to it so your controllers will simply use regular events over
run + $rootScope, but I would avoid it primarily because of $rootScope. It's essentially the "global space" of an Angular app. The only legit use I see for it is attaching event listeners ($rootScope.$on), and for broadcasting events ($rootScope.$emit).As an alternative, you could wrap your event relay in a factory.
-
As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is missing on initialization, unlike
$rootScope where it only throws when you use it.-
Factories are singletons. You don't have to worry about checking and using the existing or create a new one.
-
You're not polluting the
$rootScope nor depending on something that might not be there or have been overridden by something in a lower, enclosing scope. This is a headache to debug, especially without tools like Batarang or ngInspector.Additionally, you might also want to put your event names in a constant. That way, you have a global lookup of event names, and you won't be hardcoding string literals all over the app.
Here's an example of how it would look like as a factory (using implicit dependency injection syntax for brevity).
angular.module('SignalrModule', []);
.constant('SIGNALR_EVENTS', {
FOO_EVENT: 'fooevent',
BAR_EVENT: 'barevent',
})
.factory('SignalrFactory', function(){
return {
publish: function(){...},
subscribe: function(){...},
unsubscribe: function(){...},
};
});
angular.module('app', ['SignalrModule'])
.controller('MyController', function(SignalrFactory, SIGNALR_EVENTS){
SignalrFactory.subscribe(SIGNALR_EVENTS.FOO_EVENT, function(){
// on foo event
});
});Now I did mention that angular has a built-in pub-sub system. We can just hook on to it so your controllers will simply use regular events over
$rootScope (we're not adding stuff to $rootScope, just making it a relay for events). Not sure of the following works, but the concept is there.angular.module('SignalrModule', []);
.constant('SIGNALR_EVENTS', {
FOO_EVENT: 'fooevent',
BAR_EVENT: 'barevent',
})
.run(function($rootScope, SIGNALR_EVENTS){
// Iterate through our registry of events
Object.keys(SIGNALR_EVENTS).forEach(function(eventName){
// Relay angular events to signalr over $rootScope
$rootScope.$on(SIGNALR_EVENTS[eventName], function(){
signalR.eventAggregator.publish(SIGNALR_EVENTS[eventName]);
});
// Relay signalr events to angular over $rootScope
signalR.eventAggregator.subscribe(SIGNALR_EVENTS[eventName], function(e) {
$rootScope.$emit(SIGNALR_EVENTS[eventName], e);
});
});
});
angular.module('app', ['SignalrModule'])
.controller('MyController', function($rootScope, SIGNALR_EVENTS){
// Using regular angular-ish emit and on
$rootScope.$emit(SIGNALR_EVENTS.FOO_EVENT, { foo: 'data' });
$rootScope.$on(SIGNALR_EVENTS.BAR_EVENT, function(){
// bar event emitted somewhere
});
});Code Snippets
angular.module('SignalrModule', []);
.constant('SIGNALR_EVENTS', {
FOO_EVENT: 'fooevent',
BAR_EVENT: 'barevent',
})
.factory('SignalrFactory', function(){
return {
publish: function(){...},
subscribe: function(){...},
unsubscribe: function(){...},
};
});
angular.module('app', ['SignalrModule'])
.controller('MyController', function(SignalrFactory, SIGNALR_EVENTS){
SignalrFactory.subscribe(SIGNALR_EVENTS.FOO_EVENT, function(){
// on foo event
});
});angular.module('SignalrModule', []);
.constant('SIGNALR_EVENTS', {
FOO_EVENT: 'fooevent',
BAR_EVENT: 'barevent',
})
.run(function($rootScope, SIGNALR_EVENTS){
// Iterate through our registry of events
Object.keys(SIGNALR_EVENTS).forEach(function(eventName){
// Relay angular events to signalr over $rootScope
$rootScope.$on(SIGNALR_EVENTS[eventName], function(){
signalR.eventAggregator.publish(SIGNALR_EVENTS[eventName]);
});
// Relay signalr events to angular over $rootScope
signalR.eventAggregator.subscribe(SIGNALR_EVENTS[eventName], function(e) {
$rootScope.$emit(SIGNALR_EVENTS[eventName], e);
});
});
});
angular.module('app', ['SignalrModule'])
.controller('MyController', function($rootScope, SIGNALR_EVENTS){
// Using regular angular-ish emit and on
$rootScope.$emit(SIGNALR_EVENTS.FOO_EVENT, { foo: 'data' });
$rootScope.$on(SIGNALR_EVENTS.BAR_EVENT, function(){
// bar event emitted somewhere
});
});Context
StackExchange Code Review Q#107360, answer score: 4
Revisions (0)
No revisions yet.