diff --git a/dist/angular-validation.js b/dist/angular-validation.js index d3b5325..10adbe0 100644 --- a/dist/angular-validation.js +++ b/dist/angular-validation.js @@ -387,6 +387,7 @@ angular.module('validation.directive', ['validation.provider']); var $timeout = $injector.get('$timeout'); var $compile = $injector.get('$compile'); var $parse = $injector.get('$parse'); + var groups = {}; /** * Do this function if validation valid @@ -467,29 +468,35 @@ angular.module('validation.directive', ['validation.provider']); * Verify whether there is one of the elements inside the group valid. * If so, it returns true, otherwise, it returns false * - * @param scope - * @param element - * @param attrs - * @param ctrl + * @param validationGroup * @return {boolean} */ - var checkValidationGroup = function(scope, element, attrs, ctrl) { - var validationGroup = attrs.validationGroup; - var validationGroupElems = document.querySelectorAll('*[validation-group=' + validationGroup + ']'); - var validationGroupElem; + var checkValidationGroup = function(validationGroup) { + var group = groups[validationGroup]; - // Set the element to be invalid - ctrl.$setValidity(ctrl.$name, false); + return Object.keys(group).some(function(key) { + return group[key]; + }); + }; + + /** + * Set validity to all elements inside the given group + * + * @param scope + * @param groupName + * @param validity + */ + function setValidationGroup(scope, validationGroup, validity) { + var validationGroupElems = document.querySelectorAll('*[validation-group=' + validationGroup + ']'); // Loop through all elements inside the group for (var i = 0, len = validationGroupElems.length; i < len; i++) { - validationGroupElem = angular.element(validationGroupElems[i]); - - // If the element is valid and it's not the same element with the current checking element, returns true - if (validationGroupElem.hasClass('ng-valid') && validationGroupElem[0] !== element[0]) return true; + var elem = validationGroupElems[i]; + var formName = elem.form.name; + var elemName = elem.name; + scope[formName][elemName].$setValidity(elemName, validity); } - return false; - }; + } /** * collect elements for focus @@ -543,11 +550,23 @@ angular.module('validation.directive', ['validation.provider']); if (expression.constructor === Function) { return $q.all([$validationProvider.getExpression(validator)(value, scope, element, attrs, validatorParam)]) .then(function(data) { - if (data && data.length > 0 && data[0]) return valid.success(); - else if (validationGroup) { + if (data && data.length > 0 && data[0]) { + if (validationGroup) { + groups[validationGroup][ctrl.$name] = true; + setValidationGroup(scope, validationGroup, true); + } + return valid.success(); + } else if (validationGroup) { + groups[validationGroup][ctrl.$name] = false; + // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not. // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message. - if (!checkValidationGroup(scope, element, attrs, ctrl)) valid.error(); + if (checkValidationGroup(validationGroup)) { + setValidationGroup(scope, validationGroup, true); + } else { + setValidationGroup(scope, validationGroup, false); + return valid.error(); + } } else return valid.error(); }, function() { return valid.error(); @@ -557,12 +576,26 @@ angular.module('validation.directive', ['validation.provider']); // Check with RegExp else if (expression.constructor === RegExp) { // Only apply the test if the value is neither undefined or null - if (value !== undefined && value !== null) return $validationProvider.getExpression(validator).test(value) ? valid.success() : valid.error(); - else if (validationGroup) { - // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not. - // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message. - if (!checkValidationGroup(scope, element, attrs, ctrl)) valid.error(); - } else return valid.error(); + if (value !== undefined && value !== null) { + if ($validationProvider.getExpression(validator).test(value)) { + if (validationGroup) { + groups[validationGroup][ctrl.$name] = true; + setValidationGroup(scope, validationGroup, true); + } + return valid.success(); + } else if (validationGroup) { + groups[validationGroup][ctrl.$name] = false; + + // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not. + // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message. + if (checkValidationGroup(validationGroup)) { + setValidationGroup(scope, validationGroup, true); + } else { + setValidationGroup(scope, validationGroup, false); + return valid.error(); + } + } else return valid.error(); + } } else return valid.error(); }; @@ -622,6 +655,14 @@ angular.module('validation.directive', ['validation.provider']); initialValidity = scope.initialValidity; } + /** + * Set up groups object in order to keep track validation of elements + */ + if (validationGroup) { + if (!groups[validationGroup]) groups[validationGroup] = {}; + groups[validationGroup][ctrl.$name] = false; + } + /** * Default Valid/Invalid Message */ diff --git a/dist/angular-validation.min.js b/dist/angular-validation.min.js index b0f6f60..9c22282 100644 --- a/dist/angular-validation.min.js +++ b/dist/angular-validation.min.js @@ -1 +1 @@ -angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"]),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){return a.constructor===Function?(f.getErrorHTML=a,f):void 0},this.getErrorHTML=function(a){return'
'+a+"
"},this.setSuccessHTML=function(a){return a.constructor===Function?(f.getSuccessHTML=a,f):void 0},this.getSuccessHTML=function(a){return''+a+"
"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.addMsgElement=function(a){return a.after("")},this.getMsgElement=function(a){return a.next()},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset,addMsgElement:this.addMsgElement,getMsgElement:this.getMsgElement}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$q"),d=a.get("$timeout"),e=a.get("$compile"),f=a.get("$parse"),g=function(a,c,d,g,h,i){var j,k=c||b.getDefaultMsg(d).success,l=f(i.validCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):b.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):b.showSuccessMessage&&k?(j.html("").append(e(b.getSuccessHTML(k,a,i))(g)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!0),l(g,{message:k}),b.validCallback&&b.validCallback(a),!0},h=function(a,c,d,g,h,i){var j,k=c||b.getDefaultMsg(d).error,l=f(i.invalidCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):b.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):b.showErrorMessage&&k?(j.html("").append(e(b.getErrorHTML(k,a,i))(g)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!1),l(g,{message:k}),b.invalidCallback&&b.invalidCallback(a),!1},i=function(a,b,c,d){var e,f=c.validationGroup,g=document.querySelectorAll("*[validation-group="+f+"]");d.$setValidity(d.$name,!1);for(var h=0,i=g.length;i>h;h++)if(e=angular.element(g[h]),e.hasClass("ng-valid")&&e[0]!==b[0])return!0;return!1},j={},k=function(a,d,e,f,j,l){var m=j.slice(0),n=m[0].trim(),o=n.indexOf("="),p=-1===o?n:n.substr(0,o),q=-1===o?null:n.substr(o+1),r=m.slice(1),s=p+"SuccessMessage",t=p+"ErrorMessage",u=b.getExpression(p),v=e.validationGroup,w={success:function(){return g(d,e[s],p,a,f,e),r.length?k(a,d,e,f,r,l):!0},error:function(){return h(d,e[t],p,a,f,e)}};return void 0===u?(console.error('You are using undefined validator "%s"',p),r.length?k(a,d,e,f,r,l):void 0):u.constructor===Function?c.all([b.getExpression(p)(l,a,d,e,q)]).then(function(b){return b&&b.length>0&&b[0]?w.success():v?void(i(a,d,e,f)||w.error()):w.error()},function(){return w.error()}):u.constructor!==RegExp?w.error():void 0!==l&&null!==l?b.getExpression(p).test(l)?w.success():w.error():v?void(i(a,d,e,f)||w.error()):w.error()},l=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},m=function(){return l()+l()+l()+l()};return{restrict:"A",require:"ngModel",link:function(a,c,e,f){var g,h=e.validator,i=e.messageId,l=e.validationGroup,n=e.validMethod,o=e.ngModel,p=function(){},q=h.split(","),r=f.validationId=m();return"boolean"==typeof a.initialValidity&&(g=a.initialValidity),i||l||b.addMsgElement(c),f.$setValidity(f.$name,g),a.$on(f.$name+"reset-"+r,function(){p(),d(function(){f.$setViewValue(""),f.$setPristine(),f.$setValidity(f.$name,void 0),f.$render(),i||l?angular.element(document.querySelector("#"+(i||l))).html(""):b.getMsgElement(c).html(""),b.resetCallback&&b.resetCallback(c)})}),n=angular.isUndefined(n)?b.getValidMethod():n,a.$on(f.$name+"submit-"+r,function(b,g){var h=f.$viewValue,i=!1;i=k(a,c,e,f,q,h),"submit"===n&&(p(),p=a.$watch(function(){return a.$eval(o)},function(b,d){b!==d&&(void 0!==b&&null!==b||(b=""),i=k(a,c,e,f,q,b))}));var l=function(a){a?delete j[g]:(j[g]=c[0],d(function(){j[Math.min.apply(null,Object.keys(j))].focus()},0))};i.constructor===Object?i.then(l):l(i)}),"blur"===n?void c.bind("blur",function(){var b=a.$eval(o);a.$apply(function(){k(a,c,e,f,q,b)})}):void("submit"!==n&&"submit-only"!==n&&(a.$watch(function(){return a.$eval(o)},function(d){if(f.$pristine&&f.$viewValue)f.$setViewValue(f.$viewValue);else if(f.$pristine)return void(i||l?angular.element(document.querySelector("#"+(i||l))).html(""):b.getMsgElement(c).html(""));k(a,c,e,f,q,d)}),d(function(){e.$observe("noValidationMessage",function(a){var d;d=i||l?angular.element(document.querySelector("#"+(i||l))):b.getMsgElement(c),"true"===a||a===!0?d.css("display","none"):"false"!==a&&a!==!1||d.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this); \ No newline at end of file +angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"]),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){return a.constructor===Function?(f.getErrorHTML=a,f):void 0},this.getErrorHTML=function(a){return''+a+"
"},this.setSuccessHTML=function(a){return a.constructor===Function?(f.getSuccessHTML=a,f):void 0},this.getSuccessHTML=function(a){return''+a+"
"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.addMsgElement=function(a){return a.after("")},this.getMsgElement=function(a){return a.next()},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset,addMsgElement:this.addMsgElement,getMsgElement:this.getMsgElement}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){function b(a,b,c){for(var d=document.querySelectorAll("*[validation-group="+b+"]"),e=0,f=d.length;f>e;e++){var g=d[e],h=g.form.name,i=g.name;a[h][i].$setValidity(i,c)}}var c=a.get("$validation"),d=a.get("$q"),e=a.get("$timeout"),f=a.get("$compile"),g=a.get("$parse"),h={},i=function(a,b,d,e,h,i){var j,k=b||c.getDefaultMsg(d).success,l=g(i.validCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):c.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):c.showSuccessMessage&&k?(j.html("").append(f(c.getSuccessHTML(k,a,i))(e)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!0),l(e,{message:k}),c.validCallback&&c.validCallback(a),!0},j=function(a,b,d,e,h,i){var j,k=b||c.getDefaultMsg(d).error,l=g(i.invalidCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):c.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):c.showErrorMessage&&k?(j.html("").append(f(c.getErrorHTML(k,a,i))(e)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!1),l(e,{message:k}),c.invalidCallback&&c.invalidCallback(a),!1},k=function(a){var b=h[a];return Object.keys(b).some(function(a){return b[a]})},l={},m=function(a,e,f,g,l,n){var o=l.slice(0),p=o[0].trim(),q=p.indexOf("="),r=-1===q?p:p.substr(0,q),s=-1===q?null:p.substr(q+1),t=o.slice(1),u=r+"SuccessMessage",v=r+"ErrorMessage",w=c.getExpression(r),x=f.validationGroup,y={success:function(){return i(e,f[u],r,a,g,f),t.length?m(a,e,f,g,t,n):!0},error:function(){return j(e,f[v],r,a,g,f)}};if(void 0===w)return console.error('You are using undefined validator "%s"',r),t.length?m(a,e,f,g,t,n):void 0;if(w.constructor===Function)return d.all([c.getExpression(r)(n,a,e,f,s)]).then(function(c){return c&&c.length>0&&c[0]?(x&&(h[x][g.$name]=!0,b(a,x,!0)),y.success()):x?(h[x][g.$name]=!1,k(x)?void b(a,x,!0):(b(a,x,!1),y.error())):y.error()},function(){return y.error()});if(w.constructor!==RegExp)return y.error();if(void 0!==n&&null!==n){if(c.getExpression(r).test(n))return x&&(h[x][g.$name]=!0,b(a,x,!0)),y.success();if(!x)return y.error();if(h[x][g.$name]=!1,!k(x))return b(a,x,!1),y.error();b(a,x,!0)}},n=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},o=function(){return n()+n()+n()+n()};return{restrict:"A",require:"ngModel",link:function(a,b,d,f){var g,i=d.validator,j=d.messageId,k=d.validationGroup,n=d.validMethod,p=d.ngModel,q=function(){},r=i.split(","),s=f.validationId=o();return"boolean"==typeof a.initialValidity&&(g=a.initialValidity),k&&(h[k]||(h[k]={}),h[k][f.$name]=!1),j||k||c.addMsgElement(b),f.$setValidity(f.$name,g),a.$on(f.$name+"reset-"+s,function(){q(),e(function(){f.$setViewValue(""),f.$setPristine(),f.$setValidity(f.$name,void 0),f.$render(),j||k?angular.element(document.querySelector("#"+(j||k))).html(""):c.getMsgElement(b).html(""),c.resetCallback&&c.resetCallback(b)})}),n=angular.isUndefined(n)?c.getValidMethod():n,a.$on(f.$name+"submit-"+s,function(c,g){var h=f.$viewValue,i=!1;i=m(a,b,d,f,r,h),"submit"===n&&(q(),q=a.$watch(function(){return a.$eval(p)},function(c,e){c!==e&&((void 0===c||null===c)&&(c=""),i=m(a,b,d,f,r,c))}));var j=function(a){a?delete l[g]:(l[g]=b[0],e(function(){l[Math.min.apply(null,Object.keys(l))].focus()},0))};i.constructor===Object?i.then(j):j(i)}),"blur"===n?void b.bind("blur",function(){var c=a.$eval(p);a.$apply(function(){m(a,b,d,f,r,c)})}):void("submit"!==n&&"submit-only"!==n&&(a.$watch(function(){return a.$eval(p)},function(e){if(f.$pristine&&f.$viewValue)f.$setViewValue(f.$viewValue);else if(f.$pristine)return void(j||k?angular.element(document.querySelector("#"+(j||k))).html(""):c.getMsgElement(b).html(""));m(a,b,d,f,r,e)}),e(function(){d.$observe("noValidationMessage",function(a){var d;d=j||k?angular.element(document.querySelector("#"+(j||k))):c.getMsgElement(b),"true"===a||a===!0?d.css("display","none"):("false"===a||a===!1)&&d.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this); \ No newline at end of file diff --git a/src/validator.directive.js b/src/validator.directive.js index 58b7ca0..85a0de1 100644 --- a/src/validator.directive.js +++ b/src/validator.directive.js @@ -9,6 +9,7 @@ var $timeout = $injector.get('$timeout'); var $compile = $injector.get('$compile'); var $parse = $injector.get('$parse'); + var groups = {}; /** * Do this function if validation valid @@ -89,29 +90,35 @@ * Verify whether there is one of the elements inside the group valid. * If so, it returns true, otherwise, it returns false * - * @param scope - * @param element - * @param attrs - * @param ctrl + * @param validationGroup * @return {boolean} */ - var checkValidationGroup = function(scope, element, attrs, ctrl) { - var validationGroup = attrs.validationGroup; - var validationGroupElems = document.querySelectorAll('*[validation-group=' + validationGroup + ']'); - var validationGroupElem; + var checkValidationGroup = function(validationGroup) { + var group = groups[validationGroup]; - // Set the element to be invalid - ctrl.$setValidity(ctrl.$name, false); + return Object.keys(group).some(function(key) { + return group[key]; + }); + }; + + /** + * Set validity to all elements inside the given group + * + * @param scope + * @param groupName + * @param validity + */ + function setValidationGroup(scope, validationGroup, validity) { + var validationGroupElems = document.querySelectorAll('*[validation-group=' + validationGroup + ']'); // Loop through all elements inside the group for (var i = 0, len = validationGroupElems.length; i < len; i++) { - validationGroupElem = angular.element(validationGroupElems[i]); - - // If the element is valid and it's not the same element with the current checking element, returns true - if (validationGroupElem.hasClass('ng-valid') && validationGroupElem[0] !== element[0]) return true; + var elem = validationGroupElems[i]; + var formName = elem.form.name; + var elemName = elem.name; + scope[formName][elemName].$setValidity(elemName, validity); } - return false; - }; + } /** * collect elements for focus @@ -165,11 +172,23 @@ if (expression.constructor === Function) { return $q.all([$validationProvider.getExpression(validator)(value, scope, element, attrs, validatorParam)]) .then(function(data) { - if (data && data.length > 0 && data[0]) return valid.success(); - else if (validationGroup) { + if (data && data.length > 0 && data[0]) { + if (validationGroup) { + groups[validationGroup][ctrl.$name] = true; + setValidationGroup(scope, validationGroup, true); + } + return valid.success(); + } else if (validationGroup) { + groups[validationGroup][ctrl.$name] = false; + // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not. // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message. - if (!checkValidationGroup(scope, element, attrs, ctrl)) valid.error(); + if (checkValidationGroup(validationGroup)) { + setValidationGroup(scope, validationGroup, true); + } else { + setValidationGroup(scope, validationGroup, false); + return valid.error(); + } } else return valid.error(); }, function() { return valid.error(); @@ -179,12 +198,26 @@ // Check with RegExp else if (expression.constructor === RegExp) { // Only apply the test if the value is neither undefined or null - if (value !== undefined && value !== null) return $validationProvider.getExpression(validator).test(value) ? valid.success() : valid.error(); - else if (validationGroup) { - // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not. - // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message. - if (!checkValidationGroup(scope, element, attrs, ctrl)) valid.error(); - } else return valid.error(); + if (value !== undefined && value !== null) { + if ($validationProvider.getExpression(validator).test(value)) { + if (validationGroup) { + groups[validationGroup][ctrl.$name] = true; + setValidationGroup(scope, validationGroup, true); + } + return valid.success(); + } else if (validationGroup) { + groups[validationGroup][ctrl.$name] = false; + + // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not. + // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message. + if (checkValidationGroup(validationGroup)) { + setValidationGroup(scope, validationGroup, true); + } else { + setValidationGroup(scope, validationGroup, false); + return valid.error(); + } + } else return valid.error(); + } } else return valid.error(); }; @@ -244,6 +277,14 @@ initialValidity = scope.initialValidity; } + /** + * Set up groups object in order to keep track validation of elements + */ + if (validationGroup) { + if (!groups[validationGroup]) groups[validationGroup] = {}; + groups[validationGroup][ctrl.$name] = false; + } + /** * Default Valid/Invalid Message */ diff --git a/test/unit/validationGroupSpec.js b/test/unit/validationGroupSpec.js index c25fd1a..dea5901 100644 --- a/test/unit/validationGroupSpec.js +++ b/test/unit/validationGroupSpec.js @@ -58,6 +58,21 @@ describe('validation-group directive', function() { expect(element.hasClass('ng-invalid')).toBe(true); }); + it('should be valid when at least one of elements is valid', function() { + $scope.Form.checkbox1.$setViewValue(true); + + expect($scope.Form.$valid).toBe(true); + expect(element.hasClass('ng-valid')).toBe(true); + + $scope.Form.checkbox1.$setViewValue(false); + expect($scope.Form.$valid).toBe(false); + expect(element.hasClass('ng-invalid')).toBe(true); + + $scope.Form.checkbox2.$setViewValue(true); + expect($scope.Form.$valid).toBe(true); + expect(element.hasClass('ng-valid')).toBe(true); + }); + it('should have a success message inside the #checkbox element when an element is valid', function() { $scope.Form.checkbox1.$setViewValue(true); @@ -109,7 +124,7 @@ describe('validation-group directive', function() { $compile = $injector.get('$compile'); $scope = $rootScope.$new(); - element = $compile('')($scope); + element = $compile('')($scope); angular.element(document.body).append(element); $scope.$digest(); })); @@ -139,8 +154,8 @@ describe('validation-group directive', function() { it('should be dirty and invalid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(); - $scope.Form.telephone.$setViewValue(); + $scope.Form.email.$setViewValue(''); + $scope.Form.telephone.$setViewValue(''); expect($scope.Form.$dirty).toBe(true); expect(element.hasClass('ng-dirty')).toBe(true); @@ -148,6 +163,22 @@ describe('validation-group directive', function() { expect(element.hasClass('ng-invalid')).toBe(true); }); + it('should be valid when at least one of elements is valid', function() { + $scope.Form.email.$setViewValue('foo@bar.com'); + expect($scope.Form.$valid).toBe(true); + expect(element.hasClass('ng-valid')).toBe(true); + + $scope.Form.telephone.$setViewValue('065839481'); + $scope.Form.email.$setViewValue(''); + expect($scope.Form.$valid).toBe(true); + expect(element.hasClass('ng-valid')).toBe(true); + + $scope.Form.telephone.$setViewValue(''); + expect($scope.Form.$valid).toBe(false); + expect(element.hasClass('ng-invalid')).toBe(true); + }); + + it('should have a success message inside the #contact element when an element is valid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); @@ -157,7 +188,7 @@ describe('validation-group directive', function() { it('should have an error message inside the #contact element when no element is valid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); - $scope.Form.email.$setViewValue(); + $scope.Form.email.$setViewValue(''); messageElem = angular.element(element[0].querySelector('#contact > p')); expect(messageElem.hasClass('validation-invalid')).toBe(true); @@ -174,7 +205,7 @@ describe('validation-group directive', function() { it('should have a success message inside the #contact element when one of element is valid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(); + $scope.Form.email.$setViewValue(''); messageElem = angular.element(element[0].querySelector('#contact > p')); expect(messageElem.hasClass('validation-valid')).toBe(true); @@ -183,8 +214,8 @@ describe('validation-group directive', function() { it('should have an error message inside the #contact element when both of elements are invalid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(); - $scope.Form.telephone.$setViewValue(); + $scope.Form.email.$setViewValue(''); + $scope.Form.telephone.$setViewValue(''); messageElem = angular.element(element[0].querySelector('#contact > p')); expect(messageElem.hasClass('validation-invalid')).toBe(true); @@ -230,6 +261,45 @@ describe('validation-group directive', function() { expect(errorSpy).not.toHaveBeenCalled(); }); + it('should validate a form and call a success callback when at least one of elements in the form is valid', function() { + successSpy = jasmine.createSpy('successSpy'); + errorSpy = jasmine.createSpy('errorSpy'); + + $scope.Form.checkbox1.$setViewValue(true); + + validationProvider.validate($scope.Form) + .success(function() { + successSpy(); + }) + .error(function() { + errorSpy(); + }); + $timeout.flush(); + expect(successSpy).toHaveBeenCalled(); + expect(errorSpy).not.toHaveBeenCalled(); + }); + + it('should validate a form and call an error callback when all elements are invalid', function() { + successSpy = jasmine.createSpy('successSpy'); + errorSpy = jasmine.createSpy('errorSpy'); + + $scope.Form.checkbox1.$setViewValue(true); + $scope.Form.checkbox2.$setViewValue(true); + $scope.Form.checkbox1.$setViewValue(false); + $scope.Form.checkbox2.$setViewValue(false); + + validationProvider.validate($scope.Form) + .success(function() { + successSpy(); + }) + .error(function() { + errorSpy(); + }); + $timeout.flush(); + expect(successSpy).not.toHaveBeenCalled(); + expect(errorSpy).toHaveBeenCalled(); + }); + it('should validate a form element and call a success callback', function() { successSpy = jasmine.createSpy('successSpy'); errorSpy = jasmine.createSpy('errorSpy'); @@ -253,6 +323,7 @@ describe('validation-group directive', function() { errorSpy = jasmine.createSpy('errorSpy'); $scope.Form.checkbox1.$setViewValue(true); + $scope.Form.checkbox1.$setViewValue(false); validationProvider.validate($scope.Form) .success(function() {