diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/treeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/treeitem.directive.js index 8fa07e08bb..9c45a82af3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/treeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/treeitem.directive.js @@ -12,7 +12,7 @@ angular.module("umbraco.directives") template: '
  • ' + '' + - '' + + '' + '{{node.name}}' + '' + '
    '+ diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valregex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valregex.directive.js new file mode 100644 index 0000000000..266272e179 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valregex.directive.js @@ -0,0 +1,39 @@ +/** + * @ngdoc directive + * @name umbraco.directive:valRegex + * @restrict A + * @description A custom directive to allow for matching a value against a regex string. + * NOTE: there's already an ng-pattern but this requires that a regex expression is set, not a regex string + **/ +function valRegex() { + return { + require: 'ngModel', + restrict: "A", + link: function (scope, elm, attrs, ctrl) { + + var regex = new RegExp(scope.$eval(attrs.valRegex)); + + 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; + } + }; + + ctrl.$formatters.push(patternValidator); + ctrl.$parsers.push(patternValidator); + } + }; +} +angular.module('umbraco.directives').directive("valRegex", valRegex); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js new file mode 100644 index 0000000000..b9dd145410 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js @@ -0,0 +1,53 @@ +/** + * @ngdoc directive + * @name umbraco.directive:valServer + * @restrict A + * @description This directive is used to associate a field with a server-side validation response + * so that the validators in angular are updated based on server-side feedback. + **/ +function valServer() { + return { + require: 'ngModel', + restrict: "A", + link: function (scope, element, attr, ctrl) { + if (!scope.model || !scope.model.alias) + throw "valServer can only be used in the scope of a content property object"; + var parentErrors = scope.$parent.serverErrors; + if (!parentErrors) return; + + var fieldName = scope.$eval(attr.valServer); + + //subscribe to the changed event of the element. 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 () { + if (ctrl.$invalid) { + ctrl.$setValidity('valServer', true); + } + }); + 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 + parentErrors.subscribe(scope.model, fieldName, function (isValid, propertyErrors, allErrors) { + if (!isValid) { + ctrl.$setValidity('valServer', false); + //assign an error msg property to the current validator + ctrl.errorMsg = propertyErrors[0].errorMsg; + } + else { + ctrl.$setValidity('valServer', true); + //reset the error message + ctrl.errorMsg = ""; + } + }, true); + } + }; +} +angular.module('umbraco.directives').directive("valServer", valServer); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valsummary.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valsummary.directive.js new file mode 100644 index 0000000000..8ebff2c728 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valsummary.directive.js @@ -0,0 +1,74 @@ +/** + * @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: '', + 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); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js new file mode 100644 index 0000000000..9450f2e0b2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js @@ -0,0 +1,49 @@ +/** + * @ngdoc directive + * @name umbraco.directive:valToggleMsg + * @restrict A + * @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ? + **/ +function valToggleMsg(umbFormHelper) { + return { + restrict: "A", + link: function (scope, element, attr, ctrl) { + + if (!attr.valToggleMsg) + throw "valToggleMsg requires that a reference to a validator is specified"; + if (!attr.valMsgFor) + throw "valToggleMsg requires that the attribute valMsgFor exists on the element"; + + //create a flag for us to be able to reference in the below closures for watching. + var showValidation = false; + var hasError = false; + + var currentForm = umbFormHelper.getCurrentForm(scope); + if (!currentForm || !currentForm.$name) + throw "valToggleMsg requires that a name is assigned to the ng-form containing the validated input"; + + //add a watch to the validator for the value (i.e. $parent.myForm.value.$error.required ) + scope.$watch(currentForm.$name + "." + attr.valMsgFor + ".$error." + attr.valToggleMsg, function (isInvalid, oldValue) { + hasError = isInvalid; + if (hasError && showValidation) { + element.show(); + } + else { + element.hide(); + } + }); + + //add a watch to update our waitingOnValidation flag for use in the above closure + scope.$watch("$parent.ui.waitingOnValidation", function (isWaiting, oldValue) { + showValidation = isWaiting; + if (hasError && showValidation) { + element.show(); + } + else { + element.hide(); + } + }); + } + }; +} +angular.module('umbraco.directives').directive("valToggleMsg", valToggleMsg); \ No newline at end of file