Fixes action dialog so that it doesn't keep appending content to the container each time you move to a diff tree node and click create. Before there was nothing removing the previous $modal object from the container so you'd end up with the same create action dialog being shown for each node and then it would eventually show a munged up duplicated markup and jquery would throw a stack overflow exception.

This commit is contained in:
Shannon
2013-07-11 09:43:47 +10:00
parent 410b664f7f
commit 7395eba161
3 changed files with 127 additions and 95 deletions

View File

@@ -30,15 +30,18 @@
angular.module('umbraco.services')
.factory('dialogService', ['$rootScope', '$compile', '$http', '$timeout', '$q', '$templateCache',
function ($rootScope, $compile, $http, $timeout, $q, $templateCache) {
var _dialogs = [];
var dialogs = [];
$rootScope.$on("closeDialogs", function () {
for (var i = 0; i < _dialogs.length; i++) {
var dialog = _dialogs[i];
for (var i = 0; i < dialogs.length; i++) {
var dialog = dialogs[i];
removeDialog(dialog);
_dialogs.splice(i, 1);
dialogs.splice(i, 1);
}
});
/** Internal method that handles closing a specific dialog */
function removeDialog(dialog) {
dialog.modal("hide");
@@ -48,8 +51,8 @@ angular.module('umbraco.services')
}, 250);
}
//internal method that handles opening all dialogs
function _open(options) {
/** Internal method that handles opening all dialogs */
function openDialog(options) {
if (!options) {
options = {};
}
@@ -58,7 +61,7 @@ angular.module('umbraco.services')
container = options.container || $("body"),
animationClass = options.animation || "fade",
modalClass = options.modalClass || "umb-modal",
width = options.width || "100%";
width = options.width || "100%",
templateUrl = options.template || "views/common/notfound.html";
//if a callback is available
@@ -84,7 +87,7 @@ angular.module('umbraco.services')
.addClass(modalClass);
//push the modal into the global modal collection
_dialogs.push($modal);
dialogs.push($modal);
//if iframe is enabled, inject that instead of a template
if (options.iframe) {
@@ -99,9 +102,7 @@ angular.module('umbraco.services')
$compile($modal)(scope);
});
if (width) {
$modal.css("width", width);
}
$modal.css("width", width);
//Autoshow
if (options.show) {
@@ -109,82 +110,89 @@ angular.module('umbraco.services')
}
return $modal;
} else {
return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, { cache: true }).then(function (res) { return res.data; }))
.then(function onSuccess(template) {
}
else {
//We need to load the template with an httpget and once it's loaded we'll compile and assign the result to the container
// object. However since the result could be a promise or just data we need to use a $q.when. We still need to return the
// $modal object so we'll actually return the modal object synchronously without waiting for the promise. Otherwise this openDialog
// method will always need to return a promise which gets nasty because of promises in promises plus the result just needs a reference
// to the $modal object which will not change (only it's contents will change).
$q.when($templateCache.get(templateUrl) || $http.get(templateUrl, { cache: true }).then(function(res) { return res.data; }))
.then(function onSuccess(template) {
// Build modal object
$modal
.html(template);
// Build modal object
$modal.html(template);
//append to body or other container element
container.append($modal);
//append to body or other container element
container.append($modal);
// Compile modal content
$timeout(function () {
$compile($modal)(scope);
});
// Compile modal content
$timeout(function() {
$compile($modal)(scope);
});
//Scope to handle data from the modal form
scope.dialogData = {};
scope.dialogData.selection = [];
//Scope to handle data from the modal form
scope.dialogData = {};
scope.dialogData.selection = [];
// Provide scope display functions
//this passes the modal to the current scope
scope.$modal = function (name) {
$modal.modal(name);
};
// Provide scope display functions
//this passes the modal to the current scope
scope.$modal = function(name) {
$modal.modal(name);
};
scope.hide = function () {
$modal.modal('hide');
scope.hide = function() {
$modal.modal('hide');
$modal.remove();
$("#" + $modal.attr("id")).remove();
};
$modal.remove();
$("#" + $modal.attr("id")).remove();
};
scope.show = function () {
$modal.modal('show');
};
scope.show = function() {
$modal.modal('show');
};
scope.submit = function (data) {
if (callback) {
callback(data);
}
$modal.modal('hide');
$modal.remove();
$("#" + $modal.attr("id")).remove();
};
scope.select = function (item) {
if (scope.dialogData.selection.indexOf(item) < 0) {
scope.dialogData.selection.push(item);
}
};
scope.dismiss = scope.hide;
// Emit modal events
angular.forEach(['show', 'shown', 'hide', 'hidden'], function (name) {
$modal.on(name, function (ev) {
scope.$emit('modal-' + name, ev);
});
});
// Support autofocus attribute
$modal.on('shown', function (event) {
$('input[autofocus]', $modal).first().trigger('focus');
});
//Autoshow
if (options.show) {
$modal.modal('show');
scope.submit = function(data) {
if (callback) {
callback(data);
}
//Return the modal object
return $modal;
$modal.modal('hide');
$modal.remove();
$("#" + $modal.attr("id")).remove();
};
scope.select = function(item) {
if (scope.dialogData.selection.indexOf(item) < 0) {
scope.dialogData.selection.push(item);
}
};
scope.dismiss = scope.hide;
// Emit modal events
angular.forEach(['show', 'shown', 'hide', 'hidden'], function(name) {
$modal.on(name, function(ev) {
scope.$emit('modal-' + name, ev);
});
});
// Support autofocus attribute
$modal.on('shown', function(event) {
$('input[autofocus]', $modal).first().trigger('focus');
});
//Autoshow
if (options.show) {
$modal.modal('show');
}
});
//Return the modal object outside of the promise!
return $modal;
}
@@ -197,7 +205,8 @@ angular.module('umbraco.services')
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a modal rendering a given template url
* Opens a modal rendering a given template url.
*
* @param {Object} options rendering options
* @param {DomElement} options.container the DOM element to inject the modal into, by default set to body
* @param {Function} options.callback function called when the modal is submitted
@@ -208,10 +217,24 @@ angular.module('umbraco.services')
* @param {Int} options.width set a width on the modal, only needed for iframes
*/
open: function (options) {
return _open(options);
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#close
* @methodOf umbraco.services.dialogService
*
* @description
* Closes a specific dialog
* @param {Object} dialog the dialog object to close
*/
close: function(dialog) {
removeDialog(dialog);
},
mediaPicker: function (options) {
return _open({
return openDialog({
scope: options.scope,
callback: options.callback,
template: 'views/common/dialogs/mediaPicker.html',
@@ -219,7 +242,7 @@ angular.module('umbraco.services')
});
},
contentPicker: function (options) {
return _open({
return openDialog({
scope: options.scope,
callback: options.callback,
template: 'views/common/dialogs/contentPicker.html',
@@ -227,7 +250,7 @@ angular.module('umbraco.services')
});
},
macroPicker: function (options) {
return _open({
return openDialog({
scope: options.scope,
callback: options.callback,
template: 'views/common/dialogs/macroPicker.html',
@@ -235,7 +258,7 @@ angular.module('umbraco.services')
});
},
propertyDialog: function (options) {
return _open({
return openDialog({
scope: options.scope,
callback: options.callback,
template: 'views/common/dialogs/property.html',
@@ -246,7 +269,7 @@ angular.module('umbraco.services')
//deprecated
append: function (options) {
return _open(options);
return openDialog(options);
/*
var scope = options.scope || $rootScope.$new(),

View File

@@ -17,7 +17,7 @@
*/
angular.module('umbraco.services')
.factory('navigationService', function ($rootScope, $routeParams, $log, $location, dialogService, treeService, sectionResource) {
.factory('navigationService', function ($rootScope, $routeParams, $log, $location, $q, dialogService, treeService, sectionResource) {
var currentSection = $routeParams.section;
var currentId = $routeParams.id;
@@ -203,7 +203,7 @@ angular.module('umbraco.services')
* template is located in views/common/dialogs/user.html
*/
showUserDialog: function () {
var d = dialogService.open(
return dialogService.open(
{
template: "views/common/dialogs/user.html",
modalClass: "umb-modal-left",
@@ -243,15 +243,15 @@ angular.module('umbraco.services')
iframe = true;
}
var d = dialogService.open(
{
container: $("#dialog div.umb-panel-body"),
scope: scope,
inline: true,
show: true,
iframe: iframe,
template: templateUrl
});
return dialogService.open(
{
container: $("#dialog div.umb-panel-body"),
scope: scope,
inline: true,
show: true,
iframe: iframe,
template: templateUrl
});
},
/**

View File

@@ -9,7 +9,10 @@
*
* @param {navigationService} navigationService A reference to the navigationService
*/
function NavigationController($scope,$rootScope, $location, navigationService, historyService, sectionResource) {
function NavigationController($scope,$rootScope, $location, navigationService, dialogService, historyService, sectionResource) {
var actionDialog = null;
//load navigation service handlers
$scope.changeSection = navigationService.changeSection;
$scope.showTree = navigationService.showTree;
@@ -90,7 +93,13 @@ function NavigationController($scope,$rootScope, $location, navigationService, h
});
$scope.openDialog = function (currentNode, action, currentSection) {
navigationService.showDialog({
//ensure the actionDialog is cleared before opening another!
if (actionDialog) {
dialogService.close(actionDialog);
}
actionDialog = navigationService.showDialog({
scope: $scope,
node: currentNode,
action: action,