Fixes U4-7177 Tree dialogs disappear after opening

This commit is contained in:
Per Ploug
2015-10-08 13:12:43 +02:00
parent a199525d0f
commit ffc52d0105
2 changed files with 71 additions and 80 deletions

View File

@@ -2,13 +2,13 @@
* @ngdoc service
* @name umbraco.services.dialogService
*
* @requires $rootScope
* @requires $rootScope
* @requires $compile
* @requires $http
* @requires $log
* @requires $q
* @requires $templateCache
*
*
* @description
* Application-wide service for handling modals, overlays and dialogs
* By default it injects the passed template url into a div to body of the document
@@ -22,10 +22,10 @@
* <pre>
* var dialog = dialogService.open({template: 'path/to/page.html', show: true, callback: done});
* functon done(data){
* //The dialog has been submitted
* //The dialog has been submitted
* //data contains whatever the dialog has selected / attached
* }
* </pre>
* }
* </pre>
*/
angular.module('umbraco.services')
@@ -38,12 +38,12 @@ angular.module('umbraco.services')
for (var i = 0; i < dialogs.length; i++) {
var dialog = dialogs[i];
//very special flag which means that global events cannot close this dialog - currently only used on the login
//very special flag which means that global events cannot close this dialog - currently only used on the login
// dialog since it's special and cannot be closed without logging in.
if (!dialog.manualClose) {
dialog.close(args);
}
}
}
@@ -56,28 +56,18 @@ angular.module('umbraco.services')
//this is not entirely enough since the damn webforms scriploader still complains
if (dialog.iframe) {
dialog.element.find("iframe").attr("src", "about:blank");
$timeout(function () {
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.element.remove();
//SD: No idea why this is required but was there before - pretty sure it's not required
$("#" + dialog.element.attr("id")).remove();
dialog.scope.$destroy();
}, 1000);
} else {
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.element.remove();
//SD: No idea why this is required but was there before - pretty sure it's not required
$("#" + dialog.element.attr("id")).remove();
dialog.scope.$destroy();
}
}
//remove 'this' dialog from the dialogs array
dialogs = _.reject(dialogs, function (i) { return i === dialog; });
dialog.scope.$destroy();
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.element.remove();
//remove 'this' dialog from the dialogs array
dialogs = _.reject(dialogs, function (i) { return i === dialog; });
}
}
/** Internal method that handles opening all dialogs */
@@ -93,17 +83,17 @@ angular.module('umbraco.services')
template: "views/common/notfound.html",
callback: undefined,
closeCallback: undefined,
element: undefined,
element: undefined,
// It will set this value as a property on the dialog controller's scope as dialogData,
// used to pass in custom data to the dialog controller's $scope. Though this is near identical to
// the dialogOptions property that is also set the the dialog controller's $scope object.
// used to pass in custom data to the dialog controller's $scope. Though this is near identical to
// the dialogOptions property that is also set the the dialog controller's $scope object.
// So there's basically 2 ways of doing the same thing which we're now stuck with and in fact
// dialogData has another specially attached property called .selection which gets used.
dialogData: undefined
};
var dialog = angular.extend(defaults, options);
//NOTE: People should NOT pass in a scope object that is legacy functoinality and causes problems. We will ALWAYS
// destroy the scope when the dialog is closed regardless if it is in use elsewhere which is why it shouldn't be done.
var scope = options.scope || $rootScope.$new();
@@ -156,7 +146,7 @@ angular.module('umbraco.services')
dialog.element.css("width", dialog.width);
//Autoshow
//Autoshow
if (dialog.show) {
dialog.element.modal('show');
}
@@ -167,7 +157,7 @@ angular.module('umbraco.services')
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
// 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).
@@ -177,7 +167,7 @@ angular.module('umbraco.services')
// Build modal object
dialog.element.html(template);
//append to body or other container element
//append to body or other container element
dialog.container.append(dialog.element);
// Compile modal content
@@ -224,8 +214,8 @@ angular.module('umbraco.services')
scope.close = function (data) {
dialog.close(data);
};
//NOTE: This can ONLY ever be used to show the dialog if dialog.show is false (autoshow).
//NOTE: This can ONLY ever be used to show the dialog if dialog.show is false (autoshow).
// You CANNOT call show() after you call hide(). hide = close, they are the same thing and once
// a dialog is closed it's resources are disposed of.
scope.show = function () {
@@ -237,7 +227,7 @@ angular.module('umbraco.services')
//just show normally
dialog.element.modal('show');
}
};
scope.select = function (item) {
@@ -266,11 +256,11 @@ angular.module('umbraco.services')
dialog.scope = scope;
//Autoshow
//Autoshow
if (dialog.show) {
scope.show();
}
});
//Return the modal object outside of the promise!
@@ -368,7 +358,7 @@ angular.module('umbraco.services')
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
contentPicker: function (options) {
contentPicker: function (options) {
options.treeAlias = "content";
options.section = "content";
@@ -424,7 +414,7 @@ angular.module('umbraco.services')
* @returns {Object} modal object
*/
memberPicker: function (options) {
options.treeAlias = "member";
options.section = "member";
@@ -511,7 +501,7 @@ angular.module('umbraco.services')
* @name umbraco.services.dialogService#embedDialog
* @methodOf umbraco.services.dialogService
* @description
* Opens a dialog to an embed dialog
* Opens a dialog to an embed dialog
*/
embedDialog: function (options) {
options.template = 'views/common/dialogs/rteembed.html';
@@ -546,4 +536,4 @@ angular.module('umbraco.services')
return openDialog(options);
}
};
});
});

View File

@@ -2,14 +2,14 @@
* @ngdoc service
* @name umbraco.services.navigationService
*
* @requires $rootScope
* @requires $rootScope
* @requires $routeParams
* @requires $log
* @requires $location
* @requires dialogService
* @requires treeService
* @requires sectionResource
*
*
* @description
* Service to handle the main application navigation. Responsible for invoking the tree
* Section navigation and search, and maintain their state for the entire application lifetime
@@ -17,10 +17,10 @@
*/
function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, $injector, dialogService, umbModelMapper, treeService, notificationsService, historyService, appState, angularHelper) {
//used to track the current dialog object
var currentDialog = null;
//the main tree event handler, which gets assigned via the setupTreeEvents method
var mainTreeEventHandler = null;
//tracks the user profile dialog
@@ -35,8 +35,8 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
appState.setMenuState("showMenuDialog", false);
appState.setGlobalState("stickyNavigation", false);
appState.setGlobalState("showTray", false);
//$("#search-form input").focus();
//$("#search-form input").focus();
break;
case 'menu':
appState.setGlobalState("navMode", "menu");
@@ -87,7 +87,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
/** initializes the navigation service */
init: function() {
//keep track of the current section - initially this will always be undefined so
//keep track of the current section - initially this will always be undefined so
// no point in setting it now until it changes.
$rootScope.$watch(function () {
return $routeParams.section;
@@ -95,7 +95,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
appState.setSectionState("currentSection", newVal);
});
},
/**
@@ -107,7 +107,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
* Shows the legacy iframe and loads in the content based on the source url
* @param {String} source The URL to load into the iframe
*/
loadLegacyIFrame: function (source) {
loadLegacyIFrame: function (source) {
$location.path("/" + appState.getSectionState("currentSection") + "/framed/" + encodeURIComponent(source));
},
@@ -149,7 +149,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
showTree: function (sectionAlias, syncArgs) {
if (sectionAlias !== appState.getSectionState("currentSection")) {
appState.setSectionState("currentSection", sectionAlias);
if (syncArgs) {
this.syncTree(syncArgs);
}
@@ -165,7 +165,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
appState.setGlobalState("showTray", false);
},
/**
/**
Called to assign the main tree event handler - this is called by the navigation controller.
TODO: Potentially another dev could call this which would kind of mung the whole app so potentially there's a better way.
*/
@@ -179,7 +179,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
//when a tree node is synced this event will fire, this allows us to set the currentNode
mainTreeEventHandler.bind("treeSynced", function (ev, args) {
if (args.activate === undefined || args.activate === true) {
//set the current selected node
appState.setTreeState("selectedNode", args.node);
@@ -196,7 +196,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
//Set the current action node (this is not the same as the current selected node!)
appState.setMenuState("currentNode", args.node);
if (args.event && args.event.altKey) {
args.skipDefault = true;
}
@@ -220,7 +220,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
ev.preventDefault();
if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") {
//this is a legacy tree node!
//this is a legacy tree node!
var jsPrefix = "javascript:";
var js;
if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) {
@@ -243,7 +243,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
else if (n.routePath) {
//add action to the history service
historyService.add({ name: n.name, link: n.routePath, icon: n.icon });
//put this node into the tree state
appState.setTreeState("selectedNode", args.node);
//when a node is clicked we also need to set the active menu node to this node
@@ -269,7 +269,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
* The path format is: ["itemId","itemId"], and so on
* so to sync to a specific document type node do:
* <pre>
* navigationService.syncTree({tree: 'content', path: ["-1","123d"], forceReload: true});
* navigationService.syncTree({tree: 'content', path: ["-1","123d"], forceReload: true});
* </pre>
* @param {Object} args arguments passed to the function
* @param {String} args.tree the tree alias to sync to
@@ -287,7 +287,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
if (!args.tree) {
throw "args.tree cannot be null";
}
if (mainTreeEventHandler) {
//returns a promise
return mainTreeEventHandler.syncTree(args);
@@ -297,8 +297,8 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
return angularHelper.rejectedPromise();
},
/**
Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
/**
Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
have to set an active tree and then sync, the new API does this in one method by using syncTree
*/
_syncPath: function(path, forceReload) {
@@ -322,8 +322,8 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
}
},
/**
Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
/**
Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
have to set an active tree and then sync, the new API does this in one method by using syncTreePath
*/
_setActiveTreeType: function (treeAlias, loadChildren) {
@@ -331,7 +331,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
mainTreeEventHandler._setActiveTreeType(treeAlias, loadChildren);
}
},
/**
* @ngdoc method
* @name umbraco.services.navigationService#hideTree
@@ -356,7 +356,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
* @methodOf umbraco.services.navigationService
*
* @description
* Hides the tree by hiding the containing dom element.
* Hides the tree by hiding the containing dom element.
* This always returns a promise!
*
* @param {Event} event the click event triggering the method, passed from the DOM element
@@ -382,7 +382,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
//NOTE: This is assigning the current action node - this is not the same as the currently selected node!
appState.setMenuState("currentNode", args.node);
//ensure the current dialog is cleared before creating another!
if (currentDialog) {
dialogService.close(currentDialog);
@@ -400,13 +400,13 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
}
}
//there is no default or we couldn't find one so just continue showing the menu
//there is no default or we couldn't find one so just continue showing the menu
setMode("menu");
appState.setMenuState("currentNode", args.node);
appState.setMenuState("menuActions", data.menuItems);
appState.setMenuState("dialogTitle", args.node.name);
appState.setMenuState("dialogTitle", args.node.name);
//we're not opening a dialog, return null.
deferred.resolve(null);
@@ -437,7 +437,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
throw "action cannot be null";
}
if (!node) {
throw "node cannot be null";
throw "node cannot be null";
}
if (!section) {
throw "section cannot be null";
@@ -456,9 +456,9 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
var menuAction = action.metaData["jsAction"].split('.');
if (menuAction.length !== 2) {
//if it is not two parts long then this most likely means that it's a legacy action
//if it is not two parts long then this most likely means that it's a legacy action
var js = action.metaData["jsAction"].replace("javascript:", "");
//there's not really a different way to acheive this except for eval
//there's not really a different way to acheive this except for eval
eval(js);
}
else {
@@ -551,7 +551,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
modalClass: "umb-modal-left",
show: true
});
return service.helpDialog;
},
@@ -564,13 +564,13 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
* Opens a dialog, for a given action on a given tree node
* uses the dialogService to inject the selected action dialog
* into #dialog div.umb-panel-body
* the path to the dialog view is determined by:
* the path to the dialog view is determined by:
* "views/" + current tree + "/" + action alias + ".html"
* The dialog controller will get passed a scope object that is created here with the properties:
* scope.currentNode = the selected tree node
* scope.currentAction = the selected menu item
* so that the dialog controllers can use these properties
*
*
* @param {Object} args arguments passed to the function
* @param {Scope} args.scope current scope passed to the dialog
* @param {Object} args.action the clicked action containing `name` and `alias`
@@ -590,6 +590,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
//ensure the current dialog is cleared before creating another!
if (currentDialog) {
dialogService.close(currentDialog);
currentDialog = null;
}
setMode("dialog");
@@ -649,14 +650,14 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
}
//TODO: some action's want to launch a new window like live editing, we support this in the menu item's metadata with
// a key called: "actionUrlMethod" which can be set to either: Dialog, BlankWindow. Normally this is always set to Dialog
// if a URL is specified in the "actionUrl" metadata. For now I'm not going to implement launching in a blank window,
// a key called: "actionUrlMethod" which can be set to either: Dialog, BlankWindow. Normally this is always set to Dialog
// if a URL is specified in the "actionUrl" metadata. For now I'm not going to implement launching in a blank window,
// though would be v-easy, just not sure we want to ever support that?
var dialog = dialogService.open(
{
container: $("#dialog div.umb-modalcolumn-body"),
//The ONLY reason we're passing in scope to the dialogService (which is legacy functionality) is
//The ONLY reason we're passing in scope to the dialogService (which is legacy functionality) is
// for backwards compatibility since many dialogs require $scope.currentNode or $scope.currentAction
// to exist
scope: dialogScope,
@@ -685,9 +686,9 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
* hides the currently open dialog
*/
hideDialog: function (showMenu) {
setMode("default");
if(showMenu){
this.showMenu(undefined, { skipDefault: true, node: appState.getMenuState("currentNode") });
}