Merge pull request #3151 from umbraco/temp8-219-IsolatedContentAppForms
#219 Isolated content app forms
This commit is contained in:
@@ -16,13 +16,13 @@
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "~1.7.2",
|
||||
"angular-cookies": "~1.7.2",
|
||||
"angular-sanitize": "~1.7.2",
|
||||
"angular-touch": "~1.7.2",
|
||||
"angular-route": "~1.7.2",
|
||||
"angular-animate": "~1.7.2",
|
||||
"angular-i18n": "~1.7.2",
|
||||
"angular": "~1.7.4",
|
||||
"angular-cookies": "~1.7.4",
|
||||
"angular-sanitize": "~1.7.4",
|
||||
"angular-touch": "~1.7.4",
|
||||
"angular-route": "~1.7.4",
|
||||
"angular-animate": "~1.7.4",
|
||||
"angular-i18n": "~1.7.4",
|
||||
"signalr": "^2.2.1",
|
||||
"typeahead.js": "~0.10.5",
|
||||
"underscore": "~1.9.1",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
function ContentEditController($rootScope, $scope, $routeParams, $q, $window,
|
||||
appState, contentResource, entityResource, navigationService, notificationsService,
|
||||
serverValidationManager, contentEditingHelper, treeService, formHelper, umbRequestHelper,
|
||||
serverValidationManager, contentEditingHelper, treeService, formHelper, umbRequestHelper,
|
||||
editorState, $http, eventsService, relationResource, overlayService) {
|
||||
|
||||
var evts = [];
|
||||
@@ -56,11 +56,11 @@
|
||||
}
|
||||
|
||||
bindEvents();
|
||||
|
||||
resetVariantFlags();
|
||||
|
||||
resetVariantFlags();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This will reset isDirty flags if save is true.
|
||||
* When working with multiple variants, this will set the save/publish flags of each one to false.
|
||||
@@ -85,7 +85,7 @@
|
||||
$scope.content.variants[0].publish = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns true if the save/publish dialog should be shown when pressing the button */
|
||||
function showSaveOrPublishDialog() {
|
||||
return $scope.content.variants.length > 1;
|
||||
@@ -152,7 +152,7 @@
|
||||
*/
|
||||
function createButtons(content, app) {
|
||||
|
||||
// only create the save/publish/preview buttons if the
|
||||
// only create the save/publish/preview buttons if the
|
||||
// content app is "Conent"
|
||||
if(app && app.alias !== "umbContent" && app.alias !== "umbInfo") {
|
||||
$scope.defaultButton = null;
|
||||
@@ -180,7 +180,7 @@
|
||||
unpublish: $scope.unpublish
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$scope.defaultButton = buttons.defaultButton;
|
||||
$scope.subButtons = buttons.subButtons;
|
||||
$scope.page.showPreviewButton = true;
|
||||
@@ -210,7 +210,7 @@
|
||||
if (infiniteMode || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!$scope.content.isChildOfListView) {
|
||||
navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
$scope.page.menu.currentNode = syncArgs.node;
|
||||
@@ -221,7 +221,7 @@
|
||||
//it's a child item, just sync the ui node to the parent
|
||||
navigationService.syncTree({ tree: $scope.treeAlias, path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true });
|
||||
|
||||
//if this is a child of a list view and it's the initial load of the editor, we need to get the tree node
|
||||
//if this is a child of a list view and it's the initial load of the editor, we need to get the tree node
|
||||
// from the server so that we can load in the actions menu.
|
||||
umbRequestHelper.resourcePromise(
|
||||
$http.get(content.treeNodeUrl),
|
||||
@@ -231,9 +231,89 @@
|
||||
}
|
||||
}
|
||||
|
||||
function checkValidility(){
|
||||
//Get all controls from the 'contentForm'
|
||||
var allControls = $scope.contentForm.$getControls();
|
||||
|
||||
//An array to store items in when we find child form fields (no matter how many deep nested forms)
|
||||
var childFieldsToMarkAsValid = [];
|
||||
|
||||
//Exclude known formControls 'contentHeaderForm' and 'tabbedContentForm'
|
||||
//Check property - $name === "contentHeaderForm"
|
||||
allControls = _.filter(allControls, function(obj){
|
||||
return obj.$name !== 'contentHeaderForm' && obj.$name !== 'tabbedContentForm' && obj.hasOwnProperty('$submitted');
|
||||
});
|
||||
|
||||
for (var i = 0; i < allControls.length; i++) {
|
||||
var nestedForm = allControls[i];
|
||||
|
||||
//Get Nested Controls of this form in the loop
|
||||
var nestedFormControls = nestedForm.$getControls();
|
||||
|
||||
//Need to recurse through controls (could be more nested forms)
|
||||
childFieldsToMarkAsValid = recurseFormControls(nestedFormControls, childFieldsToMarkAsValid);
|
||||
}
|
||||
|
||||
return childFieldsToMarkAsValid;
|
||||
}
|
||||
|
||||
//Controls is the
|
||||
function recurseFormControls(controls, array){
|
||||
|
||||
//Loop over the controls
|
||||
for (var i = 0; i < controls.length; i++) {
|
||||
var controlItem = controls[i];
|
||||
|
||||
//Check if the controlItem has a property ''
|
||||
if(controlItem.hasOwnProperty('$submitted')){
|
||||
//This item is a form - so lets get the child controls of it & recurse again
|
||||
var childFormControls = controlItem.$getControls();
|
||||
recurseFormControls(childFormControls, array);
|
||||
}
|
||||
else {
|
||||
//We can assume its a field on a form
|
||||
if(controlItem.hasOwnProperty('$error')){
|
||||
//Set the validlity of the error/s to be valid
|
||||
//String of keys of error invalid messages
|
||||
var errorKeys = [];
|
||||
|
||||
for(var key in controlItem.$error){
|
||||
errorKeys.push(key);
|
||||
controlItem.$setValidity(key, true);
|
||||
}
|
||||
|
||||
//Create a basic obj - storing the control item & the error keys
|
||||
var obj = { 'control': controlItem, 'errorKeys': errorKeys };
|
||||
|
||||
//Push the updated control into the array - so we can set them back
|
||||
array.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function resetNestedFieldValiation(array){
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
var item = array[i];
|
||||
//Item is an object containing two props
|
||||
//'control' (obj) & 'errorKeys' (string array)
|
||||
var fieldControl = item.control;
|
||||
var fieldErrorKeys = item.errorKeys;
|
||||
|
||||
for(var i = 0; i < fieldErrorKeys.length; i++) {
|
||||
fieldControl.$setValidity(fieldErrorKeys[i], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
|
||||
function performSave(args) {
|
||||
|
||||
|
||||
//Used to check validility of nested form - coming from Content Apps mostly
|
||||
//Set them all to be invalid
|
||||
var fieldsToRollback = checkValidility();
|
||||
eventsService.emit("content.saving", { content: $scope.content, action: args.action });
|
||||
|
||||
return contentEditingHelper.contentEditorPerformSave({
|
||||
@@ -243,17 +323,17 @@
|
||||
action: args.action,
|
||||
showNotifications: args.showNotifications
|
||||
}).then(function (data) {
|
||||
//success
|
||||
//success
|
||||
init($scope.content);
|
||||
|
||||
syncTreeNode($scope.content, data.path);
|
||||
|
||||
eventsService.emit("content.saved", { content: $scope.content, action: args.action });
|
||||
|
||||
resetNestedFieldValiation(fieldsToRollback);
|
||||
|
||||
return $q.when(data);
|
||||
},
|
||||
function (err) {
|
||||
|
||||
syncTreeNode($scope.content, $scope.content.path);
|
||||
|
||||
//error
|
||||
@@ -261,6 +341,8 @@
|
||||
editorState.set($scope.content);
|
||||
}
|
||||
|
||||
resetNestedFieldValiation(fieldsToRollback);
|
||||
|
||||
return $q.reject(err);
|
||||
});
|
||||
}
|
||||
@@ -395,7 +477,7 @@
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
overlayService.open(dialog);
|
||||
}
|
||||
}
|
||||
@@ -534,9 +616,9 @@
|
||||
|
||||
if (!$scope.busy) {
|
||||
|
||||
// Chromes popup blocker will kick in if a window is opened
|
||||
// Chromes popup blocker will kick in if a window is opened
|
||||
// without the initial scoped request. This trick will fix that.
|
||||
//
|
||||
//
|
||||
var previewWindow = $window.open('preview/?init=true', 'umbpreview');
|
||||
|
||||
// Build the correct path so both /#/ and #/ work.
|
||||
|
||||
@@ -186,14 +186,15 @@
|
||||
} else {
|
||||
vm.openVariants[editorIndex] = variant.language.culture;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//then assign the variant to a view model to the content app
|
||||
var contentApp = _.find(variant.apps, function (a) {
|
||||
return a.alias === "umbContent";
|
||||
});
|
||||
contentApp.viewModel = variant;
|
||||
|
||||
contentApp.viewModel = _.omit(variant, 'apps');
|
||||
|
||||
// make sure the same app it set to active in the new variant
|
||||
if(activeAppAlias) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @name umbraco.directives.directive:valFormManager
|
||||
* @restrict A
|
||||
* @require formController
|
||||
* @description Used to broadcast an event to all elements inside this one to notify that form validation has
|
||||
* @description Used to broadcast an event to all elements inside this one to notify that form validation has
|
||||
* changed. If we don't use this that means you have to put a watch for each directive on a form's validation
|
||||
* changing which would result in much higher processing. We need to actually watch the whole $error collection of a form
|
||||
* because just watching $valid or $invalid doesn't acurrately trigger form validation changing.
|
||||
@@ -19,7 +19,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
var SAVED_EVENT_NAME = "formSubmitted";
|
||||
|
||||
return {
|
||||
require: ["form", "^^?valFormManager"],
|
||||
require: ["form", "^^?valFormManager", "^^?valSubView"],
|
||||
restrict: "A",
|
||||
controller: function($scope) {
|
||||
//This exposes an API for direct use with this directive
|
||||
@@ -46,9 +46,15 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
},
|
||||
link: function (scope, element, attr, ctrls) {
|
||||
|
||||
function notifySubView() {
|
||||
if (subView){
|
||||
subView.valStatusChanged({ form: formCtrl, showValidation: scope.showValidation });
|
||||
}
|
||||
}
|
||||
|
||||
var formCtrl = ctrls[0];
|
||||
var parentFormMgr = ctrls.length > 0 ? ctrls[1] : null;
|
||||
|
||||
var subView = ctrls.length > 1 ? ctrls[2] : null;
|
||||
var labels = {};
|
||||
|
||||
var labelKeys = [
|
||||
@@ -83,7 +89,9 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
return sum;
|
||||
}, function (e) {
|
||||
scope.$broadcast("valStatusChanged", { form: formCtrl });
|
||||
|
||||
|
||||
notifySubView();
|
||||
|
||||
//find all invalid elements' .control-group's and apply the error class
|
||||
var inError = element.find(".control-group .ng-invalid").closest(".control-group");
|
||||
inError.addClass("error");
|
||||
@@ -94,7 +102,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
|
||||
});
|
||||
|
||||
//This tracks if the user is currently saving a new item, we use this to determine
|
||||
//This tracks if the user is currently saving a new item, we use this to determine
|
||||
// if we should display the warning dialog that they are leaving the page - if a new item
|
||||
// is being saved we never want to display that dialog, this will also cause problems when there
|
||||
// are server side validation issues.
|
||||
@@ -104,6 +112,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
if (serverValidationManager.items.length > 0 || (parentFormMgr && parentFormMgr.showValidation)) {
|
||||
element.addClass(SHOW_VALIDATION_CLASS_NAME);
|
||||
scope.showValidation = true;
|
||||
notifySubView();
|
||||
}
|
||||
|
||||
var unsubscribe = [];
|
||||
@@ -112,6 +121,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
unsubscribe.push(scope.$on(SAVING_EVENT_NAME, function(ev, args) {
|
||||
element.addClass(SHOW_VALIDATION_CLASS_NAME);
|
||||
scope.showValidation = true;
|
||||
notifySubView();
|
||||
//set the flag so we can check to see if we should display the error.
|
||||
isSavingNewItem = $routeParams.create;
|
||||
}));
|
||||
@@ -121,8 +131,9 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
//remove validation class
|
||||
element.removeClass(SHOW_VALIDATION_CLASS_NAME);
|
||||
scope.showValidation = false;
|
||||
notifySubView();
|
||||
//clear form state as at this point we retrieve new data from the server
|
||||
//and all validation will have cleared at this point
|
||||
//and all validation will have cleared at this point
|
||||
formCtrl.$setPristine();
|
||||
}));
|
||||
|
||||
@@ -203,7 +214,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location
|
||||
$timeout(function(){
|
||||
formCtrl.$setPristine();
|
||||
}, 1000);
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,29 @@
|
||||
|
||||
function valSubViewDirective() {
|
||||
|
||||
function controller($scope, $element) {
|
||||
//expose api
|
||||
return {
|
||||
valStatusChanged: function(args) {
|
||||
if (!args.form.$valid) {
|
||||
var subViewContent = $element.find(".ng-invalid");
|
||||
|
||||
if (subViewContent.length > 0) {
|
||||
$scope.model.hasError = true;
|
||||
$scope.model.errorClass = args.showValidation ? 'show-validation' : null;
|
||||
} else {
|
||||
$scope.model.hasError = false;
|
||||
$scope.model.errorClass = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope.model.hasError = false;
|
||||
$scope.model.errorClass = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
//if there are no containing form or valFormManager controllers, then we do nothing
|
||||
@@ -43,7 +66,8 @@
|
||||
var directive = {
|
||||
require: ['?^^form', '?^^valFormManager'],
|
||||
restrict: "A",
|
||||
link: link
|
||||
link: link,
|
||||
controller: controller
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<ul class="umb-sub-views-nav" ng-show="showNavigation">
|
||||
|
||||
|
||||
<li ng-repeat="item in navigation | limitTo: itemsLimit ">
|
||||
<div ng-show="item.alias !== 'more'">
|
||||
<div ng-show="item.alias !== 'more'"
|
||||
ng-class="item.errorClass">
|
||||
<a data-element="sub-view-{{item.alias}}"
|
||||
tabindex="-1"
|
||||
class="umb-sub-views-nav-item js-umb-sub-views-nav-item"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<div class="umb-editor-sub-view"
|
||||
ng-class="'sub-view-' + model.name"
|
||||
val-sub-view>
|
||||
ng-class="'sub-view-' + model.name"
|
||||
val-sub-view>
|
||||
|
||||
<div class="umb-editor-sub-view__content"
|
||||
ng-show="model.active === true"
|
||||
ng-include="model.view">
|
||||
<div
|
||||
class="umb-editor-sub-view__content"
|
||||
ng-show="model.active === true"
|
||||
ng-include="model.view">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user