Updates eventsService to actually be able to handle async callbacks with promises - though i'm really not sure when this will effectively be used. It no longer returns a promise - since that was a 'false promise' anyways . lol.

This commit is contained in:
Shannon
2013-11-14 13:27:53 +11:00
parent 0b1eb4b42c
commit 6474b2b045
11 changed files with 283 additions and 248 deletions

View File

@@ -1,44 +1,53 @@
/* pubsub - based on https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js*/
function eventsService($q) {
var cache = {};
/** Used to broadcast and listen for global events and allow the ability to add async listeners to the callbacks */
function eventsService($q, $rootScope) {
return {
publish: function(){
var args = [].splice.call(arguments,0);
var topic = args[0];
var deferred = $q.defer();
args.splice(0,1);
/** raise an event with a given name, returns an array of promises for each listener */
publish: function (name, args) {
if(cache[topic]){
$.each(cache[topic], function() {
this.apply(null, args || []);
//there are no listeners
if (!$rootScope.$$listeners[name]) {
return [];
}
//setup a deferred promise for each listener
var deferred = [];
for (var i = 0; i < $rootScope.$$listeners[name].length; i++) {
deferred.push($q.defer());
}
//create a new event args object to pass to the
// $broadcast containing methods that will allow listeners
// to return data in an async if required
var eventArgs = {
args: args,
reject: function (a) {
deferred.pop().reject(a);
},
resolve: function (a) {
deferred.pop().resolve(a);
}
};
//send the event
$rootScope.$broadcast(name, eventArgs);
//return an array of promises
var promises = _.map(deferred, function(p) {
return p.promise;
});
deferred.resolve.apply(null, args);
}else{
deferred.resolve.apply(null, args);
}
return deferred.promise;
return promises;
},
subscribe: function(topic, callback) {
if(!cache[topic]) {
cache[topic] = [];
}
cache[topic].push(callback);
return [topic, callback];
/** subscribe to a method, or use scope.$on = same thing */
subscribe: function(name, callback) {
return $rootScope.$on(name, callback);
},
/** pass in the result of subscribe to this method, or just call the method returned from subscribe to unsubscribe */
unsubscribe: function(handle) {
var t = handle[0];
if(cache[t]){
$.each(cache[t], function(idx){
if(this === handle[1]){
cache[t].splice(idx, 1);
}
});
if (angular.isFunction(handle)) {
handle();
}
}

View File

@@ -43,7 +43,8 @@ angular.module("umbraco").controller("Umbraco.Dialogs.ContentPickerController",
args.event.preventDefault();
args.event.stopPropagation();
eventsService.publish("Umbraco.Dialogs.ContentPickerController.Select", args).then(function(args){
eventsService.publish("Umbraco.Dialogs.ContentPickerController.Select", args);
if (dialogOptions && dialogOptions.multiPicker) {
var c = $(args.event.target.parentElement);
@@ -71,6 +72,4 @@ angular.module("umbraco").controller("Umbraco.Dialogs.ContentPickerController",
}
});
});
});

View File

@@ -30,7 +30,8 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
args.event.preventDefault();
args.event.stopPropagation();
eventsService.publish("Umbraco.Dialogs.LinkPickerController.Select", args).then(function(args){
eventsService.publish("Umbraco.Dialogs.LinkPickerController.Select", args);
var c = $(args.event.target.parentElement);
//renewing
@@ -63,7 +64,6 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
$scope.selectedEl.find("i.umb-tree-icon").show();
}
}
});
});
});

View File

@@ -77,15 +77,16 @@ angular.module("umbraco")
$scope.gotoFolder(image.id);
}
else if (image.contentTypeAlias.toLowerCase() == 'image') {
eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image).then(function (img) {
eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image);
if (dialogOptions && dialogOptions.multiPicker) {
$scope.select(img);
img.cssclass = ($scope.dialogData.selection.indexOf(img) > -1) ? "selected" : "";
$scope.select(image);
image.cssclass = ($scope.dialogData.selection.indexOf(image) > -1) ? "selected" : "";
}
else {
$scope.submit(img);
$scope.submit(image);
}
});
}
ev.preventDefault();
@@ -98,14 +99,14 @@ angular.module("umbraco")
}
else if (image.contentTypeAlias.toLowerCase() == 'image') {
eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image).then(function(img) {
eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image);
if (dialogOptions && dialogOptions.multiPicker) {
$scope.select(img);
$scope.select(image);
}
else {
$scope.submit(img);
$scope.submit(image);
}
});
}
};

View File

@@ -40,29 +40,28 @@ angular.module("umbraco").controller("Umbraco.Dialogs.MemberGroupPickerControlle
args.event.stopPropagation();
eventsService.publish("Umbraco.Dialogs.MemberGroupPickerController.Select", args).then(function(a) {
eventsService.publish("Umbraco.Dialogs.MemberGroupPickerController.Select", args);
//This is a tree node, so we don't have an entity to pass in, it will need to be looked up
//from the server in this method.
select(a.node.name, a.node.id);
select(args.node.name, args.node.id);
if (dialogOptions.multiPicker) {
var c = $(a.event.target.parentElement);
if (!a.node.selected) {
a.node.selected = true;
var c = $(args.event.target.parentElement);
if (!args.node.selected) {
args.node.selected = true;
c.find("i.umb-tree-icon").hide()
.after("<i class='icon umb-tree-icon sprTree icon-check blue temporary'></i>");
}
else {
remove(a.node.name, a.node.id);
remove(args.node.name, args.node.id);
a.node.selected = false;
args.node.selected = false;
c.find(".temporary").remove();
c.find("i.umb-tree-icon").show();
}
}
});
});
});

View File

@@ -62,27 +62,26 @@ angular.module("umbraco").controller("Umbraco.Dialogs.MemberPickerController",
return;
}
eventsService.publish("Umbraco.Dialogs.MemberPickerController.Select", args).then(function(a) {
eventsService.publish("Umbraco.Dialogs.MemberPickerController.Select", args);
//This is a tree node, so we don't have an entity to pass in, it will need to be looked up
//from the server in this method.
select(a.node.name, a.node.id);
select(args.node.name, args.node.id);
if (dialogOptions && dialogOptions.multipicker) {
var c = $(a.event.target.parentElement);
if (!a.node.selected) {
a.node.selected = true;
var c = $(args.event.target.parentElement);
if (!args.node.selected) {
args.node.selected = true;
c.find("i.umb-tree-icon").hide()
.after("<i class='icon umb-tree-icon sprTree icon-check blue temporary'></i>");
}
else {
a.node.selected = false;
args.node.selected = false;
c.find(".temporary").remove();
c.find("i.umb-tree-icon").show();
}
}
});
});
});

View File

@@ -167,20 +167,21 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController",
args.event.preventDefault();
args.event.stopPropagation();
eventsService.publish("Umbraco.Dialogs.TreePickerController.Select", args).then(function (a) {
if (a.node.filtered) {
eventsService.publish("Umbraco.Dialogs.TreePickerController.Select", args);
if (args.node.filtered) {
return;
}
//This is a tree node, so we don't have an entity to pass in, it will need to be looked up
//from the server in this method.
select(a.node.name, a.node.id);
select(args.node.name, args.node.id);
//ui...
if ($scope.multiPicker) {
var c = $(a.event.target.parentElement);
if (!a.node.selected) {
a.node.selected = true;
var c = $(args.event.target.parentElement);
if (!args.node.selected) {
args.node.selected = true;
var temp = "<i class='icon umb-tree-icon sprTree icon-check blue temporary'></i>";
var icon = c.find("i.umb-tree-icon");
if (icon.length > 0) {
@@ -191,11 +192,10 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController",
}
}
else {
a.node.selected = false;
args.node.selected = false;
c.find(".temporary").remove();
c.find("i.umb-tree-icon").show();
}
}
});
});
});

View File

@@ -10,7 +10,8 @@ angular.module("umbraco")
args.event.preventDefault();
args.event.stopPropagation();
eventsService.publish("Umbraco.Editors.Content.CopyController.Select", args).then(function(args){
eventsService.publish("Umbraco.Editors.Content.CopyController.Select", args);
var c = $(args.event.target.parentElement);
if ($scope.selectedEl) {
$scope.selectedEl.find(".temporary").remove();
@@ -27,7 +28,7 @@ angular.module("umbraco")
$scope.target = args.node;
$scope.selectedEl = c;
});
});
$scope.copy = function(){

View File

@@ -10,7 +10,8 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
args.event.preventDefault();
args.event.stopPropagation();
eventsService.publish("Umbraco.Editors.Content.MoveController.Select", args).then(function(args){
eventsService.publish("Umbraco.Editors.Content.MoveController.Select", args);
var c = $(args.event.target.parentElement);
if ($scope.selectedEl) {
@@ -30,7 +31,6 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
$scope.target = args.node;
$scope.selectedEl = c;
});
});
$scope.move = function(){
contentResource.move({parentId: $scope.target.id, id: node.id})

View File

@@ -10,7 +10,8 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
args.event.preventDefault();
args.event.stopPropagation();
eventsService.publish("Umbraco.Editors.Media.MoveController.Select", args).then(function(args){
eventsService.publish("Umbraco.Editors.Media.MoveController.Select", args);
var c = $(args.event.target.parentElement);
if ($scope.selectedEl) {
$scope.selectedEl.find(".temporary").remove();
@@ -22,7 +23,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
$scope.target = args.node;
$scope.selectedEl = c;
});
});
$scope.move = function(){

View File

@@ -17,41 +17,67 @@ describe('angular event tests', function () {
val: ""
};
eventsService.subscribe("testEvent", function (args) {
args.val = "changed";
eventsService.subscribe("testEvent", function (e, args) {
args.args.val = "changed";
});
eventsService.publish("testEvent", eventArgs).then(function (args) {
eventsService.publish("testEvent", eventArgs);
expect(eventArgs.val).toBe("changed");
expect(args.val).toBe("changed");
});
$rootScope.$digest();
});
//NOTE: This will fail because our eventsService doesn't actually wait for async events to finish.
it('will handle 2 non async process', function () {
var eventArgs = {
val: ""
};
//it('will handle one async process', function () {
// var eventArgs = {
// val: ""
// };
eventsService.subscribe("testEvent", function (e, args) {
args.args.val = "changed";
});
// eventsService.subscribe("testEvent", function (args) {
// $timeout(function() {
// args.val = "changed";
// }, 1000);
// });
eventsService.subscribe("testEvent", function (e, args) {
args.args.val = "changed1";
});
// eventsService.publish("testEvent", eventArgs).then(function (args) {
// expect(eventArgs.val).toBe("changed");
// expect(args.val).toBe("changed");
// });
eventsService.publish("testEvent", eventArgs);
// $rootScope.$digest();
// $timeout.flush();
//});
expect(eventArgs.val).toBe("changed1");
it('will wait to execute after all handlers', function () {
$rootScope.$digest();
});
it('will handle one async process', function () {
var eventArgs = {
val: ""
};
eventsService.subscribe("testEvent", function (e, msg) {
$timeout(function () {
msg.args.val = "changed";
//NOTE: We could resolve anything here
msg.resolve(msg.args);
}, 1000);
});
var promises = eventsService.publish("testEvent", eventArgs);
//this won't be changed yet
expect(eventArgs.val).toBe("");
promises[0].then(function (args) {
console.log("WOOT");
expect(args.val).toBe("changed");
expect(eventArgs.val).toBe("changed");
});
$rootScope.$digest();
$timeout.flush();
});
//NOTE: This logic has been merged into the eventsService
it('POC will wait to execute after all handlers', function () {
//assign multiple listeners