diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 1d100494f6..a67f51bfed 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -14,7 +14,8 @@ controllerAs: 'vm', transclude: true, require: { - parentUmbProperty: '?^^umbProperty' + parentUmbProperty: '?^^umbProperty', + parentForm: '?^^form' }, bindings: { property: "=", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index e44e36999a..c586c88d38 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -158,19 +158,53 @@ function valPropertyMsg(serverValidationManager, localizationService, angularHel if (hadError) { scope.$evalAsync(function () { - // 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 - // form and the parent form about this will contain any invalid flags for all the other sibling properties. So when that is - // no longer invalid, we can check if we have a parent validation key (meaning we'd be nested inside of umb-property) and - // we can clear that server error. - // TODO: If there is another server error for this property though this might clear it inadvertently, at this time I'm unsure how to deal with that. - var parentValidationKey = umbPropCtrl.getParentValidationPath(); - if (parentValidationKey) { - var parentForm = formCtrl.$$parentForm; - if (parentForm && !parentForm.$invalid) { + // 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) { + + 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; } } + + //// 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 + //// form and the parent form about this will contain any invalid flags for all the other sibling properties. So when that is + //// no longer invalid, we can check if we have a parent validation key (meaning we'd be nested inside of umb-property) and + //// we can clear that server error. + //// TODO: If there is another server error for this property though this might clear it inadvertently, at this time I'm unsure how to deal with that. + //var parentValidationKey = umbPropCtrl.getParentValidationPath(); + //if (parentValidationKey) { + // // TODO: Instead of using the parent form, can we 'just' use umbProperty again which itself can check if it's + // // parent form is valid? then below we can call in a loop each parent umb property check if it has a parent validation + // // path and check if it's form is valid, this will recursively perform this logic up the chain. + // var parentForm = formCtrl.$$parentForm; + // if (parentForm && !parentForm.$invalid) { + // // TODO: Though this works for one level, if you have errors at level 1 and 2, clear errors at level when + // // and then level 2, then only the val-property-msg is cleared at level 1 and not also at level 0. + // // So we still need to recurse up the chain to deal with this + // serverValidationManager.removePropertyError(parentValidationKey, currentCulture, "", currentSegment); + // } + //} }); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js b/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js index c6382f1f73..8999efda64 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js @@ -92,29 +92,27 @@ function serverValidationManager($timeout, udiService) { }); } + /** Call all registered callbacks indicating if the data they are subscribed to is valid or invalid */ function notifyCallbacks() { for (var i = 0; i < callbacks.length; i++) { var cb = callbacks[i]; if (cb.propertyAlias === null && cb.fieldName !== null) { //its a field error callback var fieldErrors = getFieldErrors(cb.fieldName); - if (fieldErrors.length > 0) { - executeCallback(fieldErrors, cb.callback, cb.culture, cb.segment, false); - } + const valid = fieldErrors.length === 0; + executeCallback(fieldErrors, cb.callback, cb.culture, cb.segment, valid); } else if (cb.propertyAlias != null) { //its a property error var propErrors = getPropertyErrors(cb.propertyAlias, cb.culture, cb.segment, cb.fieldName); - if (propErrors.length > 0) { - executeCallback(propErrors, cb.callback, cb.culture, cb.segment, false); - } + const valid = propErrors.length === 0; + executeCallback(propErrors, cb.callback, cb.culture, cb.segment, valid); } else { //its a variant error var variantErrors = getVariantErrors(cb.culture, cb.segment); - if (variantErrors.length > 0) { - executeCallback(variantErrors, cb.callback, cb.culture, cb.segment, false); - } + const valid = variantErrors.length === 0; + executeCallback(variantErrors, cb.callback, cb.culture, cb.segment, valid); } } } @@ -678,20 +676,15 @@ function serverValidationManager($timeout, udiService) { } //remove the item + var count = items.length; items = _.reject(items, function (item) { - var found = (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === ""))); - if (found) { - //find all errors for this item - var errorsForCallback = getPropertyErrors(propertyAlias, culture, segment, fieldName); - //we should now call all of the call backs registered for this error - var cbs = getPropertyCallbacks(propertyAlias, culture, fieldName, segment); - //call each callback for this error to tell them it is now valid - for (var cb in cbs) { - executeCallback(errorsForCallback, cbs[cb].callback, culture, segment, true); - } - } - return found; + return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === ""))); }); + + if (items.length !== count) { + // removal was successful, re-notify all subscribers + notify(); + } }, reset: reset,