From 1bfab0f8a309f5c3ec4c5e8576e8ff7b075dba9c Mon Sep 17 00:00:00 2001 From: kjac Date: Wed, 21 May 2014 22:49:15 +0200 Subject: [PATCH] Optimize validation routine Only validate the property being edited, not the entire fieldset the property belongs to. --- app/directives/archetypeproperty.js | 105 ++++++++++++++-------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/app/directives/archetypeproperty.js b/app/directives/archetypeproperty.js index b65c7d1..5b3b9e6 100644 --- a/app/directives/archetypeproperty.js +++ b/app/directives/archetypeproperty.js @@ -56,9 +56,9 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c var alias = configFieldsetModel.properties[scope.propertyConfigIndex].alias; var defaultValue = configFieldsetModel.properties[scope.propertyConfigIndex].value; var umbracoPropertyAlias = scope.umbracoPropertyAlias; - // initialize container for invalid fieldset identifiers (store on ngModelCtrl to separate Archetype validations, e.g. when there two Archetype properties on the same document) - if(ngModelCtrl.invalidFieldsets == null) { - ngModelCtrl.invalidFieldsets = []; + // initialize container for invalid fieldset property identifiers (store on ngModelCtrl to separate Archetype validations, e.g. when there two Archetype properties on the same document) + if(ngModelCtrl.invalidProperties == null) { + ngModelCtrl.invalidProperties = []; } //try to convert the defaultValue to a JS object @@ -85,74 +85,77 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c var mergedConfig = _.extend(defaultConfigObj, config); - loadView(pathToView, mergedConfig, defaultValue, alias, umbracoPropertyAlias, scope, element, ngModelCtrl, validate); + loadView(pathToView, mergedConfig, defaultValue, alias, umbracoPropertyAlias, scope, element, ngModelCtrl, validateProperty); }); }); scope.$on("formSubmitting", function (ev, args) { // "hard" validate to highlight any erroneous entries - validate([scope.fieldset], true); + _.each(scope.fieldset.properties, function (property) { + validateProperty(scope.fieldset, property, true); + }); }); scope.$on("formSubmitted", function (ev, args) { - ngModelCtrl.invalidFieldsets = []; + // reset the nested fieldset validation state after submit + ngModelCtrl.invalidProperties = []; }); - function validate(renderModel, markAsInvalid){ - if(renderModel == undefined) { - return renderModel; - } + // need to pass the property fieldset here to clear any invalid state of the fieldset when validating a single fieldset property + // - it's the Umbraco way to hide the invalid state when altering an invalid property, even if the new value isn't valid either + function validateProperty(fieldset, property, markAsInvalid) { var valid = true; - _.each(renderModel, function(fieldset){ - fieldset.isValid = true; - _.each(fieldset.properties, function(property){ - property.isValid = true; - var propertyValid = true; - var propertyConfig = getPropertyByAlias(configFieldsetModel, property.alias); - if (propertyConfig) { - // use property.value !== property.value to check for NaN values on numeric inputs - if(propertyConfig.required && (property.value == null || property.value === "" || property.value !== property.value)) { - valid = false; - propertyValid = false; - } - // issue 116: RegEx validate property value - // Only validate the property value if anything has been entered - RegEx is considered a supplement to "required". - if (valid == true && propertyConfig.regEx && property.value) { - var regEx = new RegExp(propertyConfig.regEx); - if (regEx.test(property.value) == false) { - valid = false; - propertyValid = false; - } - } - // only mark the fieldset and property as invalid when doing a "hard" validation - if(propertyValid == false && markAsInvalid == true) { - fieldset.isValid = false; - property.isValid = false; - } + fieldset.isValid = true; + property.isValid = true; + var propertyConfig = getPropertyByAlias(configFieldsetModel, property.alias); + if (propertyConfig) { + // use property.value !== property.value to check for NaN values on numeric inputs + if (propertyConfig.required && (property.value == null || property.value === "" || property.value !== property.value)) { + valid = false; + } + // issue 116: RegEx validate property value + // Only validate the property value if anything has been entered - RegEx is considered a supplement to "required". + if (valid == true && propertyConfig.regEx && property.value) { + var regEx = new RegExp(propertyConfig.regEx); + if (regEx.test(property.value) == false) { + valid = false; } - }); - }); + } + // only mark the property as invalid when doing a "hard" validation + if (valid == false && markAsInvalid == true) { + property.isValid = false; + } + } - // handle nested fieldset validation by storing the identifier of all invalid fieldsets + // handle nested fieldset validation by storing the identifier of all invalid fieldset properties var fieldsetIdentifier = scope.umbracoPropertyAlias + "_" + scope.fieldsetIndex; - var fieldsetIdentifierIndex = ngModelCtrl.invalidFieldsets.indexOf(fieldsetIdentifier); - if(valid == false) { - if(fieldsetIdentifierIndex == -1) { - ngModelCtrl.invalidFieldsets.push(fieldsetIdentifier); + var propertyIdentifier = fieldsetIdentifier + "_" + property.alias; + var propertyIdentifierIndex = ngModelCtrl.invalidProperties.indexOf(propertyIdentifier); + if (valid == false) { + if (propertyIdentifierIndex == -1) { + ngModelCtrl.invalidProperties.push(propertyIdentifier); } } else { - if(fieldsetIdentifierIndex != -1) { - ngModelCtrl.invalidFieldsets.splice(fieldsetIdentifierIndex, 1); + if (propertyIdentifierIndex != -1) { + ngModelCtrl.invalidProperties.splice(propertyIdentifierIndex, 1); } } - // set invalid state if one or more fieldsets contain invalid entries - ngModelCtrl.$setValidity('validation', ngModelCtrl.invalidFieldsets.length == 0); - return renderModel; + + if (markAsInvalid) { + // mark the entire fieldset as invalid if there are any invalid properties in the fieldset, otherwise mark it as valid + fieldset.isValid = + _.find(ngModelCtrl.invalidProperties, function (item) { + return item.indexOf(fieldsetIdentifier) == 0 + }) == null; + } + + // set invalid state if one or more fieldsets contain invalid properties + ngModelCtrl.$setValidity('validation', ngModelCtrl.invalidProperties.length == 0); } } - function loadView(view, config, defaultValue, alias, umbracoPropertyAlias, scope, element, ngModelCtrl, validate) { + function loadView(view, config, defaultValue, alias, umbracoPropertyAlias, scope, element, ngModelCtrl, validateProperty) { if (view) { $http.get(view).success(function (data) { @@ -187,9 +190,9 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c scope.$watch('model.value', function (newValue, oldValue) { scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties[renderModelPropertyIndex].value = newValue; - // call validation method for this fieldset when a property value changes + // call validation method for the property when the value changes // use "soft" validation to mimic the default umbraco validation style (show error highlights on submit, not while entering data) - validate([scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex]], false); + validateProperty(scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex], scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties[renderModelPropertyIndex], false); }); element.html(data).show();