Gets group and property level validation wired up to the fields, ensures things like duplicate field warnings are created with the correct indexed field names. Fixes a few old directives to correctly watch for attribute interpolation changes which is need for some of this validation to work since it's dynamically populating the field names.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
|
||||
function LockedFieldDirective($timeout, localizationService) {
|
||||
|
||||
function link(scope, el, attr, ngModel) {
|
||||
function link(scope, el, attr, ngModel) {
|
||||
|
||||
var input = el.children('.umb-locked-field__input');
|
||||
|
||||
@@ -22,6 +22,16 @@
|
||||
scope.locked = true;
|
||||
}
|
||||
|
||||
// if regex validation is not defined as an attr set default state
|
||||
// if this is set to an empty string then regex validation can be ignored.
|
||||
if (scope.regexValidation === undefined || scope.regexValidation === null) {
|
||||
scope.regexValidation = "^[a-zA-Z]\\w.*$";
|
||||
}
|
||||
|
||||
if (scope.serverValidationField === undefined || scope.serverValidationField === null) {
|
||||
scope.serverValidationField = "";
|
||||
}
|
||||
|
||||
// if locked state is not defined as an attr set default state
|
||||
if (scope.placeholderText === undefined || scope.placeholderText === null) {
|
||||
scope.placeholderText = "Enter value...";
|
||||
@@ -70,7 +80,9 @@
|
||||
scope: {
|
||||
model: '=ngModel',
|
||||
locked: "=?",
|
||||
placeholderText: "=?"
|
||||
placeholderText: "=?",
|
||||
regexValidation: "=?",
|
||||
serverValidationField: "@"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ angular.module('umbraco.directives.validation')
|
||||
}
|
||||
};
|
||||
validators[key] = validateFn;
|
||||
ctrl.$formatters.push(validateFn);
|
||||
|
||||
ctrl.$parsers.push(validateFn);
|
||||
});
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ function valHighlight($timeout) {
|
||||
restrict: "A",
|
||||
link: function (scope, element, attrs, ctrl) {
|
||||
|
||||
scope.$watch(function() {
|
||||
return scope.$eval(attrs.valHighlight);
|
||||
}, function(newVal, oldVal) {
|
||||
attrs.$observe("valHighlight", function (newVal) {
|
||||
if (newVal === true) {
|
||||
element.addClass("highlight-error");
|
||||
$timeout(function () {
|
||||
@@ -23,7 +21,7 @@ function valHighlight($timeout) {
|
||||
element.removeClass("highlight-error");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ function valEmail(valEmailExpression) {
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(patternValidator);
|
||||
ctrl.$parsers.push(patternValidator);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,9 +59,6 @@ function valPropertyValidator(serverValidationManager) {
|
||||
}
|
||||
};
|
||||
|
||||
// Formatters are invoked when the model is modified in the code.
|
||||
modelCtrl.$formatters.push(validate);
|
||||
|
||||
// Parsers are called as soon as the value in the form input is modified
|
||||
modelCtrl.$parsers.push(validate);
|
||||
|
||||
|
||||
@@ -13,50 +13,51 @@ function valRegex() {
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
|
||||
var flags = "";
|
||||
if (attrs.valRegexFlags) {
|
||||
try {
|
||||
flags = scope.$eval(attrs.valRegexFlags);
|
||||
if (!flags) {
|
||||
flags = attrs.valRegexFlags;
|
||||
var regex;
|
||||
|
||||
attrs.$observe("valRegexFlags", function (newVal) {
|
||||
if (newVal) {
|
||||
flags = newVal;
|
||||
}
|
||||
});
|
||||
|
||||
attrs.$observe("valRegex", function (newVal) {
|
||||
if (newVal) {
|
||||
try {
|
||||
var resolved = newVal;
|
||||
if (resolved) {
|
||||
regex = new RegExp(resolved, flags);
|
||||
}
|
||||
else {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
flags = attrs.valRegexFlags;
|
||||
}
|
||||
}
|
||||
var regex;
|
||||
try {
|
||||
var resolved = scope.$eval(attrs.valRegex);
|
||||
if (resolved) {
|
||||
regex = new RegExp(resolved, flags);
|
||||
}
|
||||
else {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
});
|
||||
|
||||
var patternValidator = function (viewValue) {
|
||||
//NOTE: we don't validate on empty values, use required validator for that
|
||||
if (!viewValue || regex.test(viewValue)) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('valRegex', true);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "";
|
||||
return viewValue;
|
||||
}
|
||||
else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('valRegex', false);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "Value is invalid, it does not match the correct pattern";
|
||||
return undefined;
|
||||
if (regex) {
|
||||
//NOTE: we don't validate on empty values, use required validator for that
|
||||
if (!viewValue || regex.test(viewValue)) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('valRegex', true);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "";
|
||||
return viewValue;
|
||||
}
|
||||
else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('valRegex', false);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "Value is invalid, it does not match the correct pattern";
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(patternValidator);
|
||||
ctrl.$parsers.push(patternValidator);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,47 +11,46 @@ function valServerField(serverValidationManager) {
|
||||
restrict: "A",
|
||||
link: function (scope, element, attr, ctrl) {
|
||||
|
||||
if (!attr.valServerField) {
|
||||
throw "valServerField must have a field name for referencing server errors";
|
||||
}
|
||||
var fieldName = null;
|
||||
|
||||
var fieldName = attr.valServerField;
|
||||
var evalfieldName = scope.$eval(attr.valServerField);
|
||||
if (evalfieldName) {
|
||||
fieldName = evalfieldName;
|
||||
}
|
||||
attr.$observe("valServerField", function (newVal) {
|
||||
if (newVal && fieldName === null) {
|
||||
fieldName = newVal;
|
||||
|
||||
//subscribe to the changed event of the view model. This is required because when we
|
||||
// have a server error we actually invalidate the form which means it cannot be
|
||||
// resubmitted. So once a field is changed that has a server error assigned to it
|
||||
// we need to re-validate it for the server side validator so the user can resubmit
|
||||
// the form. Of course normal client-side validators will continue to execute.
|
||||
ctrl.$viewChangeListeners.push(function () {
|
||||
if (ctrl.$invalid) {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
}
|
||||
});
|
||||
|
||||
//subscribe to the server validation changes
|
||||
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
ctrl.$setValidity('valServerField', false);
|
||||
//assign an error msg property to the current validator
|
||||
ctrl.errorMsg = fieldErrors[0].errorMsg;
|
||||
}
|
||||
else {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
//reset the error message
|
||||
ctrl.errorMsg = "";
|
||||
}
|
||||
});
|
||||
|
||||
//when the element is disposed we need to unsubscribe!
|
||||
// NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
|
||||
// but they are a different callback instance than the above.
|
||||
element.bind('$destroy', function () {
|
||||
serverValidationManager.unsubscribe(null, fieldName);
|
||||
});
|
||||
|
||||
//subscribe to the changed event of the view model. This is required because when we
|
||||
// have a server error we actually invalidate the form which means it cannot be
|
||||
// resubmitted. So once a field is changed that has a server error assigned to it
|
||||
// we need to re-validate it for the server side validator so the user can resubmit
|
||||
// the form. Of course normal client-side validators will continue to execute.
|
||||
ctrl.$viewChangeListeners.push(function () {
|
||||
if (ctrl.$invalid) {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
}
|
||||
});
|
||||
|
||||
//subscribe to the server validation changes
|
||||
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
ctrl.$setValidity('valServerField', false);
|
||||
//assign an error msg property to the current validator
|
||||
ctrl.errorMsg = fieldErrors[0].errorMsg;
|
||||
}
|
||||
else {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
//reset the error message
|
||||
ctrl.errorMsg = "";
|
||||
}
|
||||
});
|
||||
|
||||
//when the element is disposed we need to unsubscribe!
|
||||
// NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
|
||||
// but they are a different callback instance than the above.
|
||||
element.bind('$destroy', function () {
|
||||
serverValidationManager.unsubscribe(null, fieldName);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{'Groups[' + $index + '].Name'}}" />
|
||||
|
||||
<span class="help-inline" val-msg-for="groupName" val-toggle-msg="valServerField"></span>
|
||||
</div>
|
||||
</ng-form>
|
||||
|
||||
@@ -93,13 +95,21 @@
|
||||
|
||||
<ng-form name="propertyTypeForm">
|
||||
<div class="control-group -no-margin" ng-if="!sortingMode">
|
||||
<div class="umb-group-builder__property-meta-alias">{{ property.alias }}</div>
|
||||
|
||||
<umb-locked-field locked="locked"
|
||||
ng-model="property.alias"
|
||||
placeholder-text="'Alias...'"
|
||||
server-validation-field="{{'Groups[' + $parent.$index + '].Properties[' + $index + '].Alias'}}">
|
||||
</umb-locked-field>
|
||||
|
||||
<div class="umb-group-builder__property-meta-label">
|
||||
<textarea placeholder="Label..." ng-model="property.label" ng-disabled="property.inherited"
|
||||
name="groupName"
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{'Groups[' + $parent.$index + '].Properties[' + $index + '].Label'}}"></textarea>
|
||||
|
||||
<span class="help-inline" val-msg-for="groupName" val-toggle-msg="valServerField"></span>
|
||||
</div>
|
||||
|
||||
<div class="umb-group-builder__property-meta-description">
|
||||
|
||||
@@ -8,20 +8,21 @@
|
||||
<i class="umb-locked-field__lock-icon icon-unlocked -unlocked"></i>
|
||||
</a>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
class="umb-locked-field__input"
|
||||
name="lockedField"
|
||||
ng-model="model"
|
||||
ng-disabled="locked"
|
||||
ng-class="{'-unlocked': !locked}"
|
||||
placeholder="{{placeholderText}}"
|
||||
umb-auto-resize
|
||||
required
|
||||
val-regex="^[a-zA-Z]\w.*$"
|
||||
/>
|
||||
|
||||
<input type="text"
|
||||
class="umb-locked-field__input"
|
||||
name="lockedField"
|
||||
ng-model="model"
|
||||
ng-disabled="locked"
|
||||
ng-class="{'-unlocked': !locked}"
|
||||
placeholder="{{placeholderText}}"
|
||||
val-regex="{{regexValidation}}"
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{serverValidationField}}"
|
||||
/>
|
||||
|
||||
<span class="validation-label" val-msg-for="lockedField" val-toggle-msg="required"><localize key="required" /></span>
|
||||
<span class="validation-label" val-msg-for="lockedField" val-toggle-msg="valRegex">Invalid alias</span>
|
||||
<span ng-if="regexValidation.length > 0" class="validation-label" val-msg-for="lockedField" val-toggle-msg="valRegex">Invalid alias</span>
|
||||
<span ng-if="serverValidationField.length > 0" class="validation-label" val-msg-for="lockedField" val-toggle-msg="valServerField"></span>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user