Merge pull request #3151 from umbraco/temp8-219-IsolatedContentAppForms

#219 Isolated content app forms
This commit is contained in:
Warren Buckley
2018-10-04 12:53:38 +01:00
committed by GitHub
7 changed files with 159 additions and 39 deletions

View File

@@ -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",

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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);
}
};
}

View File

@@ -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;

View File

@@ -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"

View File

@@ -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>