WIP - with lots of notes
This commit is contained in:
@@ -69,6 +69,18 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.trimStartSpecial) {
|
||||
/** trimSpecial extension method for string */
|
||||
// Removes all non printable chars from beginning of a string
|
||||
String.prototype.trimStartSpecial = function () {
|
||||
var index = 0;
|
||||
while (this.charCodeAt(index) <= 46) {
|
||||
index++;
|
||||
}
|
||||
return this.substr(index);
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.startsWith) {
|
||||
/** startsWith extension method for string */
|
||||
String.prototype.startsWith = function (str) {
|
||||
|
||||
@@ -158,33 +158,33 @@ function valPropertyMsg(serverValidationManager, localizationService, angularHel
|
||||
if (hadError) {
|
||||
scope.$evalAsync(function () {
|
||||
|
||||
// TODO: This does not work :( :( :(
|
||||
// We cannot clear a val-property-msg because another nested child might have server validation errors too.
|
||||
// I 'think' we might be able to set the UI validation of this val-property-msg based on the child validators as well
|
||||
// as the server validator so it can 'just' unset itself if all child validators are cleared. Can it be done?
|
||||
//// TODO: This does not work :( :( :(
|
||||
//// We cannot clear a val-property-msg because another nested child might have server validation errors too.
|
||||
//// I 'think' we might be able to set the UI validation of this val-property-msg based on the child validators as well
|
||||
//// as the server validator so it can 'just' unset itself if all child validators are cleared. Can it be done?
|
||||
|
||||
// Here we loop over the umbProperty hierarchy to see if we should clear the val-property-msg server validation key.
|
||||
// we will clear the key if the parent for is valid, or if the parent form is only invalid due to a single val-property-msg error.
|
||||
var currUmbProperty = umbPropCtrl;
|
||||
var parentValidationKey = currUmbProperty.getParentValidationPath();
|
||||
while (currUmbProperty && parentValidationKey) {
|
||||
//// Here we loop over the umbProperty hierarchy to see if we should clear the val-property-msg server validation key.
|
||||
//// we will clear the key if the parent for is valid, or if the parent form is only invalid due to a single val-property-msg error.
|
||||
//var currUmbProperty = umbPropCtrl;
|
||||
//var parentValidationKey = currUmbProperty.getParentValidationPath();
|
||||
//while (currUmbProperty && parentValidationKey) {
|
||||
|
||||
if (!currUmbProperty.parentForm.$invalid || (_.keys(currUmbProperty.parentForm.$error).length === 1 && currUmbProperty.parentForm.$error.valPropertyMsg)) {
|
||||
serverValidationManager.removePropertyError(parentValidationKey, currentCulture, "", currentSegment);
|
||||
// if (!currUmbProperty.parentForm.$invalid || (_.keys(currUmbProperty.parentForm.$error).length === 1 && currUmbProperty.parentForm.$error.valPropertyMsg)) {
|
||||
// serverValidationManager.removePropertyError(parentValidationKey, currentCulture, "", currentSegment);
|
||||
|
||||
// re-assign and loop
|
||||
if (currUmbProperty !== umbPropCtrl.parentUmbProperty) {
|
||||
currUmbProperty = umbPropCtrl.parentUmbProperty;
|
||||
parentValidationKey = currUmbProperty ? currUmbProperty.getParentValidationPath() : null;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// // re-assign and loop
|
||||
// if (currUmbProperty !== umbPropCtrl.parentUmbProperty) {
|
||||
// currUmbProperty = umbPropCtrl.parentUmbProperty;
|
||||
// parentValidationKey = currUmbProperty ? currUmbProperty.getParentValidationPath() : null;
|
||||
// }
|
||||
// else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
//// we need to navigate the parentForm here, unfortunately there's no real alternative unless we create our own directive
|
||||
//// of some sort but that would also get messy. This works though since in this case we're always going to be in the property
|
||||
@@ -205,19 +205,43 @@ function valPropertyMsg(serverValidationManager, localizationService, angularHel
|
||||
// serverValidationManager.removePropertyError(parentValidationKey, currentCulture, "", currentSegment);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//function checkFormValidation(f) {
|
||||
|
||||
// if (!angularHelper.isForm(f)) {
|
||||
// throw "The object is not an angular Form";
|
||||
// }
|
||||
|
||||
// // if there's no value, the controls can be reset, which clears the error state on formCtrl
|
||||
// for (let control of formCtrl.$getControls()) {
|
||||
// control.$setValidity();
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
function checkValidationStatus() {
|
||||
if (formCtrl.$invalid) {
|
||||
//first we need to check if the valPropertyMsg validity is invalid
|
||||
if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) {
|
||||
//since we already have an error we'll just return since this means we've already set the
|
||||
// hasError and errorMsg properties which occurs below in the serverValidationManager.subscribe
|
||||
|
||||
// TODO: This does not work! we cannot just clear if there are no errors in child controls because child controls
|
||||
// won't even have been loaded yet so this will just instantly clear them
|
||||
//// At this stage we might have an error assigned because it was assigned based on validation hierarchy from the server,
|
||||
//// BUT one or ALL of the child server (and client) validations may be cleared at this point. We will know if we have an
|
||||
//// explicitly assigned error based on the error message assigned, if it is a non-explicit error (meaning that the error
|
||||
//// was assigned because it has child errors) then the message will just be: labels.propertyHasErrors
|
||||
//if (scope.errorMsg === labels.propertyHasErrors && _.every(formCtrl.$getControls(), c => c.$valid)) {
|
||||
// resetError();
|
||||
//}
|
||||
|
||||
return;
|
||||
}
|
||||
//if there are any errors in the current property form that are not valPropertyMsg
|
||||
@@ -295,7 +319,7 @@ function valPropertyMsg(serverValidationManager, localizationService, angularHel
|
||||
// indicate that a content property is invalid at the property level since developers may not actually implement
|
||||
// the correct field validation in their property editors.
|
||||
|
||||
if (scope.currentProperty) { //this can be null if no property was assigned, TODO: I don't believe it can? If it was null we'd get errors above
|
||||
if (scope.currentProperty) { //this can be null if no property was assigned
|
||||
|
||||
function serverValidationManagerCallback(isValid, propertyErrors, allErrors) {
|
||||
hasError = !isValid;
|
||||
@@ -311,13 +335,13 @@ function valPropertyMsg(serverValidationManager, localizationService, angularHel
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribe.push(serverValidationManager.subscribe(propertyValidationKey,
|
||||
unsubscribe.push(serverValidationManager.subscribe(
|
||||
propertyValidationKey,
|
||||
currentCulture,
|
||||
"",
|
||||
serverValidationManagerCallback,
|
||||
currentSegment
|
||||
)
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
//when the scope is disposed we need to unsubscribe
|
||||
|
||||
@@ -55,11 +55,8 @@ function valServer(serverValidationManager) {
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertyValidationKey() {
|
||||
// Get the property validation path if there is one, this is how wiring up any nested/virtual property validation works
|
||||
var propertyValidationPath = umbPropCtrl ? umbPropCtrl.getValidationPath() : null;
|
||||
return propertyValidationPath ? propertyValidationPath : currentProperty.alias;
|
||||
}
|
||||
// Get the property validation path if there is one, this is how wiring up any nested/virtual property validation works
|
||||
var propertyValidationPath = umbPropCtrl ? umbPropCtrl.getValidationPath() : currentProperty.alias;
|
||||
|
||||
// Need to watch the value model for it to change, previously we had subscribed to
|
||||
// modelCtrl.$viewChangeListeners but this is not good enough if you have an editor that
|
||||
@@ -81,10 +78,10 @@ function valServer(serverValidationManager) {
|
||||
|
||||
if (modelCtrl.$invalid) {
|
||||
modelCtrl.$setValidity('valServer', true);
|
||||
console.log("valServer cleared (watch)");
|
||||
console.log("valServer cleared (watch) " + propertyValidationPath);
|
||||
|
||||
//clear the server validation entry
|
||||
serverValidationManager.removePropertyError(getPropertyValidationKey(), currentCulture, fieldName, currentSegment);
|
||||
serverValidationManager.removePropertyError(propertyValidationPath, currentCulture, fieldName, currentSegment);
|
||||
stopWatch();
|
||||
}
|
||||
}, true);
|
||||
@@ -102,14 +99,14 @@ function valServer(serverValidationManager) {
|
||||
function serverValidationManagerCallback(isValid, propertyErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
modelCtrl.$setValidity('valServer', false);
|
||||
console.log("valServer error");
|
||||
console.log("valServer error " + propertyValidationPath);
|
||||
//assign an error msg property to the current validator
|
||||
modelCtrl.errorMsg = propertyErrors[0].errorMsg;
|
||||
startWatch();
|
||||
}
|
||||
else {
|
||||
modelCtrl.$setValidity('valServer', true);
|
||||
console.log("valServer cleared");
|
||||
console.log("valServer cleared " + propertyValidationPath);
|
||||
//reset the error message
|
||||
modelCtrl.errorMsg = "";
|
||||
stopWatch();
|
||||
@@ -119,7 +116,7 @@ function valServer(serverValidationManager) {
|
||||
|
||||
|
||||
unsubscribe.push(serverValidationManager.subscribe(
|
||||
getPropertyValidationKey(),
|
||||
propertyValidationPath,
|
||||
currentCulture,
|
||||
fieldName,
|
||||
serverValidationManagerCallback,
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
* Some angular helper/extension methods
|
||||
*/
|
||||
function angularHelper($q) {
|
||||
|
||||
var requiredFormProps = ["$error", "$name", "$dirty", "$pristine", "$valid", "$submitted", "$pending"];
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
@@ -100,6 +103,28 @@ function angularHelper($q) {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
isForm: function (obj) {
|
||||
|
||||
// a method to check that the collection of object prop names contains the property name expected
|
||||
function allPropertiesExist(objectPropNames) {
|
||||
//ensure that every required property name exists on the current object
|
||||
return _.every(requiredFormProps, function (item) {
|
||||
return _.contains(objectPropNames, item);
|
||||
});
|
||||
}
|
||||
|
||||
//get the keys of the property names for the current object
|
||||
var props = _.keys(obj);
|
||||
//if the length isn't correct, try the next prop
|
||||
if (props.length < requiredFormProps.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//ensure that every required property name exists on the current scope property
|
||||
return allPropertiesExist(props);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name getCurrentForm
|
||||
@@ -121,31 +146,10 @@ function angularHelper($q) {
|
||||
// is to inject the $element object and use: $element.inheritedData('$formController');
|
||||
|
||||
var form = null;
|
||||
var requiredFormProps = ["$error", "$name", "$dirty", "$pristine", "$valid", "$submitted", "$pending"];
|
||||
|
||||
// a method to check that the collection of object prop names contains the property name expected
|
||||
function propertyExists(objectPropNames) {
|
||||
//ensure that every required property name exists on the current scope property
|
||||
return _.every(requiredFormProps, function (item) {
|
||||
|
||||
return _.contains(objectPropNames, item);
|
||||
});
|
||||
}
|
||||
|
||||
for (var p in scope) {
|
||||
|
||||
if (_.isObject(scope[p]) && p !== "this" && p.substr(0, 1) !== "$") {
|
||||
//get the keys of the property names for the current property
|
||||
var props = _.keys(scope[p]);
|
||||
//if the length isn't correct, try the next prop
|
||||
if (props.length < requiredFormProps.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//ensure that every required property name exists on the current scope property
|
||||
var containProperty = propertyExists(props);
|
||||
|
||||
if (containProperty) {
|
||||
if (this.isForm(scope[p])) {
|
||||
form = scope[p];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -614,6 +614,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
formHelper.handleServerValidation(args.err.data.ModelState);
|
||||
|
||||
//add model state errors to notifications
|
||||
// TODO: Need to ignore complex messages
|
||||
if (args.showNotifications) {
|
||||
for (var e in args.err.data.ModelState) {
|
||||
notificationsService.error("Validation", args.err.data.ModelState[e][0]);
|
||||
|
||||
@@ -54,8 +54,13 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
}
|
||||
}
|
||||
|
||||
//reset the server validations
|
||||
serverValidationManager.reset();
|
||||
//reset the server validations if required (default is true), otherwise notify existing ones of changes
|
||||
if (!args.keepServerValidation) {
|
||||
serverValidationManager.reset();
|
||||
}
|
||||
else {
|
||||
serverValidationManager.notify();
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -10,9 +10,29 @@
|
||||
*/
|
||||
function serverValidationManager($timeout, udiService) {
|
||||
|
||||
// TODO: It would be nicer to just SHA1 hash these 'keys' instead of having the keys be compound values
|
||||
// It would be another lib dependency or we could embed this https://github.com/emn178/js-sha1
|
||||
// this would remove the need for the auto-generated 'id' values since the hash would be the actual id value.
|
||||
|
||||
// The array of callback objects, each object 'key' is:
|
||||
// - propertyAlias
|
||||
// - culture
|
||||
// - fieldName
|
||||
// - segment
|
||||
// The object also contains:
|
||||
// - callback (function)
|
||||
// - id (unique identifier, auto-generated, used internally for unsubscribing the callback)
|
||||
var callbacks = [];
|
||||
|
||||
// The array of error messages
|
||||
// The array of error message objects, each object 'key' is:
|
||||
// - propertyAlias
|
||||
// - culture
|
||||
// - fieldName
|
||||
// - segment
|
||||
// The object also contains:
|
||||
// - errorMsg
|
||||
// - id (unique identifier, auto-generated, used internally for mapping parent/child property validation messages)
|
||||
// - parentId (used to map parent/child property validation messages)
|
||||
var items = [];
|
||||
|
||||
/** calls the callback specified with the errors specified, used internally */
|
||||
@@ -41,6 +61,13 @@ function serverValidationManager($timeout, udiService) {
|
||||
*/
|
||||
function notify() {
|
||||
$timeout(function () {
|
||||
|
||||
console.log(`VAL-ERROR-COUNT: ${items.length}`);
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
console.log(`VAL-ERROR [${item.propertyAlias}] [${item.culture}] [${item.fieldName}] [${item.segment}]`)
|
||||
}
|
||||
|
||||
notifyCallbacks();
|
||||
});
|
||||
}
|
||||
@@ -76,7 +103,14 @@ function serverValidationManager($timeout, udiService) {
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getPropertyErrorById(id) {
|
||||
//find all errors for this id
|
||||
return _.find(items, function (item) {
|
||||
return (item.id === id);
|
||||
});
|
||||
}
|
||||
|
||||
function getVariantErrors(culture, segment) {
|
||||
|
||||
if (!culture) {
|
||||
@@ -118,35 +152,47 @@ function serverValidationManager($timeout, udiService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dictionary of id (of the block) and it's corresponding validation ModelState
|
||||
* Flattens the complex errror result json into an array of the block's id/parent id and it's corresponding validation ModelState
|
||||
* @param {any} errorMsg
|
||||
* @param {any} the id of the parentId (if any)
|
||||
*/
|
||||
function parseComplexEditorError(errorMsg) {
|
||||
function parseComplexEditorError(errorMsg, parentId) {
|
||||
|
||||
var json = JSON.parse(errorMsg);
|
||||
var json = Utilities.isArray(errorMsg) ? errorMsg : JSON.parse(errorMsg);
|
||||
|
||||
var result = {};
|
||||
var result = [];
|
||||
|
||||
function extractModelState(validation) {
|
||||
function extractModelState(validation, pid) {
|
||||
if (validation.$id && validation.ModelState) {
|
||||
result[validation.$id] = validation.ModelState;
|
||||
var ms = {
|
||||
id: validation.$id,
|
||||
elementUdi: udiService.build("element", validation.$id),
|
||||
parentId: pid,
|
||||
modelState: validation.ModelState
|
||||
};
|
||||
result.push(ms);
|
||||
return ms;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function iterateErrorBlocks(blocks) {
|
||||
function iterateErrorBlocks(blocks, pid) {
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
var validation = blocks[i];
|
||||
extractModelState(validation);
|
||||
var ms = extractModelState(validation, pid);
|
||||
if (!ms) {
|
||||
continue;
|
||||
}
|
||||
var nested = _.omit(validation, "$id", "$elementTypeAlias", "ModelState");
|
||||
for (const [key, value] of Object.entries(nested)) {
|
||||
if (Array.isArray(value)) {
|
||||
iterateErrorBlocks(value); // recurse
|
||||
iterateErrorBlocks(value, ms.id); // recurse
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterateErrorBlocks(json);
|
||||
iterateErrorBlocks(json, parentId);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -258,7 +304,7 @@ function serverValidationManager($timeout, udiService) {
|
||||
* @description
|
||||
* Adds an error message for the content property
|
||||
*/
|
||||
function addPropertyError(propertyAlias, culture, fieldName, errorMsg, segment) {
|
||||
function addPropertyError(propertyAlias, culture, fieldName, errorMsg, segment, parentId) {
|
||||
|
||||
if (!propertyAlias) {
|
||||
return;
|
||||
@@ -277,14 +323,19 @@ function serverValidationManager($timeout, udiService) {
|
||||
errorMsg = "";
|
||||
}
|
||||
|
||||
// if the error message is json it's a complex editor validation response that we need to parse
|
||||
if (errorMsg.startsWith("[")) {
|
||||
var id = String.CreateGuid();
|
||||
|
||||
var idsToErrors = parseComplexEditorError(errorMsg);
|
||||
for (const [key, value] of Object.entries(idsToErrors)) {
|
||||
const elementUdi = udiService.build("element", key);
|
||||
addErrorsForModelState(value, elementUdi);
|
||||
}
|
||||
// remove all non printable chars and whitespace from the string
|
||||
if (Utilities.isString(errorMsg)) {
|
||||
errorMsg = errorMsg.trimStartSpecial().trim();
|
||||
}
|
||||
|
||||
// if the error message is json it's a complex editor validation response that we need to parse
|
||||
if ((Utilities.isString(errorMsg) && errorMsg.startsWith("[")) || Utilities.isArray(errorMsg)) {
|
||||
|
||||
var idsToErrors = parseComplexEditorError(errorMsg, id);
|
||||
console.log("idsToErrors = " + JSON.stringify(idsToErrors));
|
||||
idsToErrors.forEach(x => addErrorsForModelState(x.modelState, x.elementUdi, x.parentId));
|
||||
|
||||
// We need to clear the error message else it will show up as a giant json block against the property
|
||||
errorMsg = "";
|
||||
@@ -293,6 +344,8 @@ function serverValidationManager($timeout, udiService) {
|
||||
//only add the item if it doesn't exist
|
||||
if (!hasPropertyError(propertyAlias, culture, fieldName, segment)) {
|
||||
items.push({
|
||||
id: id,
|
||||
parentId: parentId,
|
||||
propertyAlias: propertyAlias,
|
||||
culture: culture,
|
||||
segment: segment,
|
||||
@@ -368,10 +421,16 @@ function serverValidationManager($timeout, udiService) {
|
||||
* @methodOf umbraco.services.serverValidationManager
|
||||
* @param {any} modelState
|
||||
* @param {any} elementUdi optional parameter specifying a nested element's UDI for which this property belongs (for complex editors)
|
||||
* @param {any} parentId optional parameter specifying the parentId validation item object (for complex editors)
|
||||
* @description
|
||||
* This wires up all of the server validation model state so that valServer and valServerField directives work
|
||||
*/
|
||||
function addErrorsForModelState(modelState, elementUdi) {
|
||||
function addErrorsForModelState(modelState, elementUdi, parentId) {
|
||||
|
||||
if (!Utilities.isObject(modelState)) {
|
||||
throw "modelState is not an object";
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(modelState)) {
|
||||
|
||||
//This is where things get interesting....
|
||||
@@ -437,7 +496,7 @@ function serverValidationManager($timeout, udiService) {
|
||||
}
|
||||
|
||||
// add a generic error for the property
|
||||
addPropertyError(propertyValidationKey, culture, htmlFieldReference, value && Array.isArray(value) && value.length > 0 ? value[0] : null, segment);
|
||||
addPropertyError(propertyValidationKey, culture, htmlFieldReference, value && Array.isArray(value) && value.length > 0 ? value[0] : null, segment, parentId);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -541,6 +600,8 @@ function serverValidationManager($timeout, udiService) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`serverValidationMgr subscribed [${propertyAlias}] [${culture}] [${fieldName}] [${segment}]`);
|
||||
|
||||
var id = String.CreateGuid();
|
||||
|
||||
//normalize culture to "invariant"
|
||||
@@ -700,23 +761,15 @@ function serverValidationManager($timeout, udiService) {
|
||||
* Gets the error message for the content property
|
||||
*/
|
||||
getPropertyError: function (propertyAlias, culture, fieldName, segment) {
|
||||
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
var errors = getPropertyErrors(propertyAlias, culture, segment, fieldName);
|
||||
if (errors.length > 0) { // should only ever contain one
|
||||
return errors[0];
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
var err = _.find(items, function (item) {
|
||||
//return true if the property alias matches and if an empty field name is specified or the field name matches
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
return err;
|
||||
return undefined;
|
||||
},
|
||||
|
||||
|
||||
getPropertyErrorById: getPropertyErrorById,
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name getFieldError
|
||||
|
||||
@@ -56,7 +56,8 @@ angular.module("umbraco")
|
||||
|
||||
vm.submitAndClose = function () {
|
||||
if (vm.model && vm.model.submit) {
|
||||
if (formHelper.submitForm({ scope: $scope })) {
|
||||
// always keep server validations since this will be a nested editor and server validations are global
|
||||
if (formHelper.submitForm({ scope: $scope, formCtrl: vm.blockForm, keepServerValidation: true })) {
|
||||
vm.model.submit(vm.model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<div class="umb-block-editor" ng-controller="Umbraco.Editors.BlockEditorController as vm">
|
||||
|
||||
<ng-form name="blockForm" val-form-manager>
|
||||
<ng-form name="vm.blockForm" val-form-manager>
|
||||
|
||||
<umb-editor-view umb-tabs ng-if="!page.loading">
|
||||
|
||||
<pre style="background-color:aquamarine">{{vm.blockForm.$valid}}</pre>
|
||||
|
||||
<umb-editor-header
|
||||
name="vm.model.title"
|
||||
name-locked="true"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
<pre>{{ vm.getValidationPath() }}</pre>
|
||||
|
||||
<!-- TODO: Should we disable watching complex values if we know this is a complex editor? -->
|
||||
<val-property-msg></val-property-msg>
|
||||
|
||||
<div class="umb-el-wrap">
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
beforeEach(module('umbraco.services'));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
serverValidationManager = $injector.get('serverValidationManager');
|
||||
serverValidationManager = $injector.get('serverValidationManager');
|
||||
serverValidationManager.clear();
|
||||
}));
|
||||
|
||||
describe('managing field validation errors', function () {
|
||||
@@ -316,6 +317,51 @@
|
||||
|
||||
describe('managing complex editor validation errors', function () {
|
||||
|
||||
// this root element doesn't have it's own attached errors, instead it has model state just
|
||||
// showing that it has errors within it's nested properties. that ModelState is automatically
|
||||
// added on the server side.
|
||||
var nonRootLevelComplexValidationMsg = `[
|
||||
{
|
||||
"$elementTypeAlias": "addressBook",
|
||||
"$id": "34E3A26C-103D-4A05-AB9D-7E14032309C3",
|
||||
"addresses":
|
||||
[
|
||||
{
|
||||
"$elementTypeAlias": "addressInfo",
|
||||
"$id": "FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1",
|
||||
"ModelState":
|
||||
{
|
||||
"_Properties.city.invariant.null.country": [
|
||||
"City is not in Australia"
|
||||
],
|
||||
"_Properties.city.invariant.null.capital": [
|
||||
"Not a capital city"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"$elementTypeAlias": "addressInfo",
|
||||
"$id": "7170A4DD-2441-4B1B-A8D3-437D75C4CBC9",
|
||||
"ModelState":
|
||||
{
|
||||
"_Properties.city.invariant.null.country": [
|
||||
"City is not in Australia"
|
||||
],
|
||||
"_Properties.city.invariant.null.capital": [
|
||||
"Not a capital city"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"ModelState":
|
||||
{
|
||||
"_Properties.addresses.invariant.null": [
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
]`;
|
||||
|
||||
it('create dictionary of id to ModelState', function () {
|
||||
|
||||
//arrange
|
||||
@@ -365,80 +411,52 @@
|
||||
]`;
|
||||
|
||||
//act
|
||||
var ids = serverValidationManager.parseComplexEditorError(complexValidationMsg);
|
||||
var ms = serverValidationManager.parseComplexEditorError(complexValidationMsg);
|
||||
|
||||
//assert
|
||||
var keys = Object.keys(ids);
|
||||
|
||||
expect(keys.length).toEqual(3);
|
||||
expect(keys[0]).toEqual("34E3A26C-103D-4A05-AB9D-7E14032309C3");
|
||||
expect(keys[1]).toEqual("FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1");
|
||||
expect(keys[2]).toEqual("7170A4DD-2441-4B1B-A8D3-437D75C4CBC9");
|
||||
expect(ms.length).toEqual(3);
|
||||
expect(ms[0].id).toEqual("34E3A26C-103D-4A05-AB9D-7E14032309C3");
|
||||
expect(ms[1].id).toEqual("FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1");
|
||||
expect(ms[2].id).toEqual("7170A4DD-2441-4B1B-A8D3-437D75C4CBC9");
|
||||
|
||||
});
|
||||
|
||||
it('create dictionary of id to ModelState with inherited errors', function () {
|
||||
|
||||
// arrange
|
||||
// this root element doesn't have it's own attached errors, instead it has model state just
|
||||
// showing that it has errors within it's nested properties. that ModelState is automatically
|
||||
// added on the server side.
|
||||
var complexValidationMsg = `[
|
||||
{
|
||||
"$elementTypeAlias": "addressBook",
|
||||
"$id": "34E3A26C-103D-4A05-AB9D-7E14032309C3",
|
||||
"addresses":
|
||||
[
|
||||
{
|
||||
"$elementTypeAlias": "addressInfo",
|
||||
"$id": "FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1",
|
||||
"ModelState":
|
||||
{
|
||||
"_Properties.city.invariant.null.country": [
|
||||
"City is not in Australia"
|
||||
],
|
||||
"_Properties.city.invariant.null.capital": [
|
||||
"Not a capital city"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"$elementTypeAlias": "addressInfo",
|
||||
"$id": "7170A4DD-2441-4B1B-A8D3-437D75C4CBC9",
|
||||
"ModelState":
|
||||
{
|
||||
"_Properties.city.invariant.null.country": [
|
||||
"City is not in Australia"
|
||||
],
|
||||
"_Properties.city.invariant.null.capital": [
|
||||
"Not a capital city"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"ModelState":
|
||||
{
|
||||
"_Properties.addresses.invariant.null": [
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
]`;
|
||||
|
||||
//act
|
||||
var ids = serverValidationManager.parseComplexEditorError(complexValidationMsg);
|
||||
var ms = serverValidationManager.parseComplexEditorError(nonRootLevelComplexValidationMsg);
|
||||
|
||||
//assert
|
||||
var keys = Object.keys(ids);
|
||||
|
||||
expect(keys.length).toEqual(3);
|
||||
expect(keys[0]).toEqual("34E3A26C-103D-4A05-AB9D-7E14032309C3");
|
||||
var item0ModelState = ids["34E3A26C-103D-4A05-AB9D-7E14032309C3"];
|
||||
expect(ms.length).toEqual(3);
|
||||
expect(ms[0].id).toEqual("34E3A26C-103D-4A05-AB9D-7E14032309C3");
|
||||
var item0ModelState = ms[0].modelState;
|
||||
expect(Object.keys(item0ModelState).length).toEqual(1);
|
||||
expect(item0ModelState["_Properties.addresses.invariant.null"].length).toEqual(1);
|
||||
expect(item0ModelState["_Properties.addresses.invariant.null"][0]).toEqual("");
|
||||
expect(keys[1]).toEqual("FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1");
|
||||
expect(keys[2]).toEqual("7170A4DD-2441-4B1B-A8D3-437D75C4CBC9");
|
||||
expect(ms[1].id).toEqual("FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1");
|
||||
expect(ms[2].id).toEqual("7170A4DD-2441-4B1B-A8D3-437D75C4CBC9");
|
||||
|
||||
});
|
||||
|
||||
it('add errors for ModelState with inherited errors', function () {
|
||||
|
||||
//act
|
||||
let modelState = {
|
||||
"_Properties.blockFeatures.invariant.null": [
|
||||
nonRootLevelComplexValidationMsg
|
||||
]
|
||||
};
|
||||
serverValidationManager.addErrorsForModelState(modelState);
|
||||
|
||||
//assert
|
||||
console.log(JSON.stringify(serverValidationManager.items));
|
||||
let propError = serverValidationManager.getPropertyError("umb://element/FBEAEE8F4BC943EE8B81FCA8978850F1/city");
|
||||
expect(propError).toBeDefined();
|
||||
console.log(JSON.stringify(propError));
|
||||
expect(propError.parentId).toBeDefined();
|
||||
let parentError = serverValidationManager.getPropertyErrorById(propError.parentId);
|
||||
expect(parentError).toBeDefined();
|
||||
expect(parentError.propertyAlias).toEqual("umb://element/34E3A26C103D4A05AB9D7E14032309C3/addresses");
|
||||
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user