Implemented the required/regex validation that is applied to PropertyType's on a Document type (this might be temporary if we get around to implementing pre-value overrides). Changed the name from ManifestValueValidator to just ValueValidator and allows for property editors to override the defaults if they need to change how the regex/required validation is processed against the property's value. Have streamlined all of the validation directives to require an FormController which we then listen to. Have removed valBubble as we don't actually need that functionality anymore and this simplfies input elements too.
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name umbraco.directive:valBubble
|
||||
* @restrict A
|
||||
* @description This directive will bubble up a notification via an emit event (upwards)
|
||||
describing the state of the validation element. This is useful for
|
||||
parent elements to know about child element validation state.
|
||||
**/
|
||||
function valBubble() {
|
||||
return {
|
||||
require: ['ngModel', '^form'],
|
||||
link: function (scope, element, attr, ctrls) {
|
||||
|
||||
if (!attr.name) {
|
||||
throw "valBubble must be set on an input element that has a 'name' attribute";
|
||||
}
|
||||
|
||||
var modelCtrl = ctrls[0];
|
||||
var formCtrl = ctrls[1];
|
||||
|
||||
//watch the current form's validation for the current field name
|
||||
scope.$watch(formCtrl.$name + "." + modelCtrl.$name + ".$valid", function (isValid, lastValue) {
|
||||
if (isValid !== undefined) {
|
||||
//emit an event upwards
|
||||
scope.$emit("valBubble", {
|
||||
isValid: isValid, // if the field is valid
|
||||
element: element, // the element that the validation applies to
|
||||
expression: this.exp, // the expression that was watched to check validity
|
||||
scope: scope, // the current scope
|
||||
formCtrl: formCtrl // the current form controller
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.directives').directive("valBubble", valBubble);
|
||||
@@ -4,15 +4,17 @@
|
||||
* @restrict A
|
||||
* @description This directive is used to control the display of the property level validation message.
|
||||
* We will listen for server side validation changes
|
||||
* and when an error is detected for this property we'll show the error message and then we need
|
||||
* to emit the valBubble event so that any parent listening can update it's UI (like the validation summary)
|
||||
* and when an error is detected for this property we'll show the error message
|
||||
**/
|
||||
function valPropertyMsg(serverValidationService) {
|
||||
return {
|
||||
return {
|
||||
scope: {
|
||||
currentProperty: "=property"
|
||||
},
|
||||
require: "^form", //require that this directive is contained within an ngForm
|
||||
replace: true, //replace the element with the template
|
||||
restrict: "E", //restrict to element
|
||||
template: "<div ng-show=\"errorMsg != ''\" class='alert alert-error' >{{errorMsg}}</div>",
|
||||
template: "<div ng-show=\"errorMsg != ''\" class='alert alert-error property-error' >{{errorMsg}}</div>",
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -26,11 +28,9 @@ function valPropertyMsg(serverValidationService) {
|
||||
* @param formCtrl {FormController} Our directive requries a reference to a form controller which gets passed in to this parameter
|
||||
*/
|
||||
link: function (scope, element, attrs, formCtrl) {
|
||||
|
||||
if (!attrs.property) {
|
||||
throw "the valPropertyMsg requires an attribute 'property' set which equals the current content property object";
|
||||
}
|
||||
var currentProperty = scope.$eval(attrs.property);
|
||||
|
||||
//assign the form control to our isolated scope so we can watch it's values
|
||||
scope.formCtrl = formCtrl;
|
||||
|
||||
//flags for use in the below closures
|
||||
var showValidation = false;
|
||||
@@ -39,19 +39,26 @@ function valPropertyMsg(serverValidationService) {
|
||||
//create properties on our custom scope so we can use it in our template
|
||||
scope.errorMsg = "";
|
||||
|
||||
//listen for form validation
|
||||
scope.$watch(formCtrl.$name + ".$valid", function (isValid, oldValue) {
|
||||
if (isValid === undefined) {
|
||||
//listen for error changes
|
||||
scope.$watch("formCtrl.$error", function () {
|
||||
if (formCtrl.$valid === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
//check if it's one of the properties that is invalid in the current content property
|
||||
if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) {
|
||||
if (!formCtrl.$valid) {
|
||||
|
||||
//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 serverValidationService.subscribe
|
||||
return;
|
||||
}
|
||||
else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) {
|
||||
//check if it's one of the properties that is invalid in the current content property
|
||||
hasError = true;
|
||||
if (showValidation && scope.errorMsg === "") {
|
||||
//update the validation message
|
||||
scope.errorMsg = serverValidationService.getError(currentProperty, "");
|
||||
//update the validation message if we don't already have one assigned.
|
||||
if (showValidation && scope.errorMsg === "") {
|
||||
scope.errorMsg = serverValidationService.getError(scope.currentProperty, "");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -63,13 +70,13 @@ function valPropertyMsg(serverValidationService) {
|
||||
hasError = false;
|
||||
scope.errorMsg = "";
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
//listen for the forms saving event
|
||||
scope.$on("saving", function (ev, args) {
|
||||
showValidation = true;
|
||||
if (hasError && scope.errorMsg === "") {
|
||||
scope.errorMsg = serverValidationService.getError(currentProperty, "");
|
||||
scope.errorMsg = serverValidationService.getError(scope.currentProperty, "");
|
||||
}
|
||||
else if (!hasError) {
|
||||
scope.errorMsg = "";
|
||||
@@ -80,45 +87,51 @@ function valPropertyMsg(serverValidationService) {
|
||||
scope.$on("saved", function (ev, args) {
|
||||
showValidation = false;
|
||||
scope.errorMsg = "";
|
||||
formCtrl.$setValidity('valPropertyMsg', true);
|
||||
});
|
||||
|
||||
//We need to subscribe to any changes to our model (based on user input)
|
||||
// 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.
|
||||
scope.$watch("currentProperty.value", function(newValue) {
|
||||
if (formCtrl.$invalid) {
|
||||
scope.errorMsg = "";
|
||||
formCtrl.$setValidity('valPropertyMsg', true);
|
||||
}
|
||||
});
|
||||
|
||||
//listen for server validation changes
|
||||
// NOTE: we pass in "" in order to listen for all validation changes to the content property, not for
|
||||
// validation changes to fields in the property this is because some server side validators may not
|
||||
// return the field name for which the error belongs too, just the property for which it belongs.
|
||||
serverValidationService.subscribe(currentProperty, "", function (isValid, propertyErrors, allErrors) {
|
||||
serverValidationService.subscribe(scope.currentProperty, "", function (isValid, propertyErrors, allErrors) {
|
||||
hasError = !isValid;
|
||||
if (hasError) {
|
||||
//set the error message to the server message
|
||||
scope.errorMsg = propertyErrors[0].errorMsg;
|
||||
scope.errorMsg = propertyErrors[0].errorMsg;
|
||||
//now that we've used the server validation message, we need to remove it from the
|
||||
//error collection... it is a 'one-time' usage so that when the field is invalidated
|
||||
//again, the message we display is the client side message.
|
||||
//NOTE: 'this' in the subscribe callback context is the validation manager object.
|
||||
this.removeError(scope.model);
|
||||
//emit an event upwards
|
||||
scope.$emit("valBubble", {
|
||||
isValid: false, // it is INVALID
|
||||
element: element, // the element that the validation applies to
|
||||
scope: scope, // the scope
|
||||
formCtrl: formCtrl // the current form controller
|
||||
});
|
||||
this.removeError(scope.currentProperty);
|
||||
//flag that the current validator is invalid
|
||||
formCtrl.$setValidity('valPropertyMsg', false);
|
||||
}
|
||||
else {
|
||||
scope.errorMsg = "";
|
||||
//emit an event upwards
|
||||
scope.$emit("valBubble", {
|
||||
isValid: true, // it is VALID
|
||||
element: element, // the element that the validation applies to
|
||||
scope: scope, // the scope
|
||||
formCtrl: formCtrl // the current form controller
|
||||
});
|
||||
//flag that the current validator is valid
|
||||
formCtrl.$setValidity('valPropertyMsg', true);
|
||||
}
|
||||
});
|
||||
|
||||
//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 () {
|
||||
serverValidationService.unsubscribe(currentProperty, "");
|
||||
serverValidationService.unsubscribe(scope.currentProperty, "");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,23 +22,19 @@ function valServer(serverValidationService) {
|
||||
fieldName = attr.valServer;
|
||||
}
|
||||
|
||||
//subscribe to the changed event of the element. This is required because when we
|
||||
//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.
|
||||
element.keydown(function () {
|
||||
ctrl.$viewChangeListeners.push(function () {
|
||||
if (ctrl.$invalid) {
|
||||
ctrl.$setValidity('valServer', true);
|
||||
//emit the event upwards
|
||||
scope.$emit("serverRevalidated", { modelCtrl: ctrl });
|
||||
}
|
||||
});
|
||||
element.change(function () {
|
||||
if (ctrl.$invalid) {
|
||||
ctrl.$setValidity('valServer', true);
|
||||
}
|
||||
});
|
||||
//TODO: DO we need to watch for other changes on the element ?
|
||||
|
||||
|
||||
//subscribe to the server validation changes
|
||||
serverValidationService.subscribe(currentProperty, fieldName, function (isValid, propertyErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
@@ -54,6 +50,8 @@ function valServer(serverValidationService) {
|
||||
});
|
||||
|
||||
//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 () {
|
||||
serverValidationService.unsubscribe(currentProperty, fieldName);
|
||||
});
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name umbraco.directive:valSummary
|
||||
* @restrict E
|
||||
* @description This directive will display a validation summary for the current form based on the
|
||||
content properties of the current content item.
|
||||
**/
|
||||
function valSummary() {
|
||||
return {
|
||||
scope: true, // create a new scope for this directive
|
||||
replace: true, // replace the html element with the template
|
||||
restrict: "E", // restrict to an element
|
||||
template: '<ul class="validation-summary"><li ng-repeat="model in validationSummary">{{model}}</li></ul>',
|
||||
link: function (scope, element, attr, ctrl) {
|
||||
|
||||
//create properties on our custom scope so we can use it in our template
|
||||
scope.validationSummary = [];
|
||||
|
||||
//create a flag for us to be able to reference in the below closures for watching.
|
||||
var showValidation = false;
|
||||
|
||||
//add a watch to update our waitingOnValidation flag for use in the below closures
|
||||
scope.$watch("$parent.ui.waitingOnValidation", function (isWaiting, oldValue) {
|
||||
showValidation = isWaiting;
|
||||
if (scope.validationSummary.length > 0 && showValidation) {
|
||||
element.show();
|
||||
}
|
||||
else {
|
||||
element.hide();
|
||||
}
|
||||
});
|
||||
|
||||
//if we are to show field property based errors.
|
||||
//this requires listening for bubbled events from valBubble directive.
|
||||
|
||||
scope.$parent.$on("valBubble", function (evt, args) {
|
||||
var msg = "The value assigned for the property " + args.scope.model.label + " is invalid";
|
||||
var exists = _.contains(scope.validationSummary, msg);
|
||||
|
||||
//we need to check if the entire property is valid, even though the args says this field is valid there
|
||||
// may be multiple values attached to a content property. The easiest way to do this is check the DOM
|
||||
// just like we are doing for the property level validation message.
|
||||
var propertyHasErrors = args.element.closest(".content-property").find(".ng-invalid").length > 0;
|
||||
|
||||
if (args.isValid && exists && !propertyHasErrors) {
|
||||
//it is valid but we have a val msg for it so we'll need to remove the message
|
||||
scope.validationSummary = _.reject(scope.validationSummary, function (item) {
|
||||
return item === msg;
|
||||
});
|
||||
}
|
||||
else if (!args.isValid && !exists) {
|
||||
//it is invalid and we don't have a msg for it already
|
||||
scope.validationSummary.push(msg);
|
||||
}
|
||||
|
||||
//show the summary if there are errors and the form has been submitted
|
||||
if (showValidation && scope.validationSummary.length > 0) {
|
||||
element.show();
|
||||
}
|
||||
});
|
||||
//listen for form invalidation so we know when to hide it
|
||||
scope.$watch("contentForm.$error", function (errors) {
|
||||
//check if there is an error and hide the summary if not
|
||||
var hasError = _.find(errors, function (err) {
|
||||
return (err.length && err.length > 0);
|
||||
});
|
||||
if (!hasError) {
|
||||
element.hide();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.directives').directive("valSummary", valSummary);
|
||||
@@ -12,7 +12,7 @@ function valToggleMsg() {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name link
|
||||
* @methodOf valPropertyMsg
|
||||
* @methodOf valServer
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
|
||||
Reference in New Issue
Block a user