From 18765a8dd986856a9fa176fc4835d90d25f663b2 Mon Sep 17 00:00:00 2001 From: Ghislain B Date: Sun, 29 Mar 2015 00:41:35 -0400 Subject: [PATCH] Enhancement #13 - Display errors on Submit - Can now also display all errors on a Submit - Minified script is now englobed under 1 and only 1 file (angular-validation.min.js) --- .gitignore | 5 + app.js | 45 +++++-- bower.json | 2 +- changelog.txt | 3 +- ...llin1.min.js => angular-validation.min.js} | 10 +- dist/validation-common.min.js | 9 -- dist/validation-directive.min.js | 1 - dist/validation-rules.min.js | 1 - dist/validation-service.min.js | 1 - gulpfile.js | 87 +++++-------- index.html | 34 ++--- locales/validation/en.json | 10 +- locales/validation/fr.json | 12 +- package.json | 31 +++++ readme.md | 94 +++++++++----- src/validation-common.js | 121 ++++++++++-------- src/validation-directive.js | 22 ++-- src/validation-rules.js | 74 +++++------ src/validation-service.js | 75 +++++++---- style.css | 11 +- templates/testingFormDirective.html | 63 ++++----- templates/testingFormService.html | 63 ++++----- 22 files changed, 436 insertions(+), 338 deletions(-) rename dist/{angular-validation-allin1.min.js => angular-validation.min.js} (66%) delete mode 100644 dist/validation-common.min.js delete mode 100644 dist/validation-directive.min.js delete mode 100644 dist/validation-rules.min.js delete mode 100644 dist/validation-service.min.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 38a363a..770de10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +################# +## NPM Package +################# +node_modules/ + ################# ## Eclipse ################# diff --git a/app.js b/app.js index a66d657..d4851c8 100644 --- a/app.js +++ b/app.js @@ -7,7 +7,7 @@ myApp.config(['$compileProvider', '$locationProvider', '$routeProvider', functio $routeProvider .when('/validate-directive', { templateUrl: 'templates/testingFormDirective.html', - controller: 'Ctrl' + controller: 'CtrlValidationDirective' }) .when('/validate-service', { templateUrl: 'templates/testingFormService.html', @@ -15,36 +15,45 @@ myApp.config(['$compileProvider', '$locationProvider', '$routeProvider', functio }) .otherwise({ redirectTo: 'validate-directive', - }); + }); }]) .config(['$translateProvider', function ($translateProvider) { $translateProvider.useStaticFilesLoader({ prefix: 'locales/validation/', suffix: '.json' }); - + // load English ('en') table on startup $translateProvider.preferredLanguage('en'); }]); -// -- Main Controller for Angular-Validation Directive +// -- Main page Controller // --------------------------------------------------- myApp.controller('Ctrl', ['$location', '$route', '$scope', '$translate', function ($location, $route, $scope, $translate) { // change the translation language & reload the page to make sure all errors were rendered properly - $scope.switchLanguage = function (key) { + $scope.switchLanguage = function (key) { $translate.use(key).then(function() { $route.reload(); - }); + }); }; $scope.goto = function ( path ) { $location.path( path ); }; +}]); + +// -- Controller to use Angular-Validation Directive +// ----------------------------------------------- +myApp.controller('CtrlValidationDirective', ['$scope', 'validationService', function ($scope, validationService) { + $scope.submitForm = function() { + if(new validationService().checkFormValidity($scope.form1)) { + alert('All good, proceed with submit...'); + } + } $scope.showValidationSummary = function () { $scope.displayValidationSummary = true; - } + } }]); - // -- Controller to use Angular-Validation Service // ----------------------------------------------- @@ -53,7 +62,7 @@ myApp.controller('CtrlValidationService', ['$scope', '$translate', 'validationSe // start by creating the service var myValidation = new validationService(); - // you can create indepent call to the validation service + // you can create indepent call to the validation service myValidation.addValidator({ elmName: 'input2', debounce: 3000, @@ -67,7 +76,7 @@ myApp.controller('CtrlValidationService', ['$scope', '$translate', 'validationSe // #1 .addValidtor('myElementName', 'myRules') ... #2 .addValidator({ elmName: 'inputX', rules: 'myRules'}) // the available object properties are the exact same set as the directive except that they are camelCase myValidation - .setGlobalOptions({ debounce: 1500, scope: $scope }) + .setGlobalOptions({ debounce: 1500, scope: $scope }) .addValidator('input3', 'float_signed|between_num:-0.6,99.5|required') .addValidator('input4', 'exact_len:4|regex:YYWW:=^(0[9]|1[0-9]|2[0-9]|3[0-9])(5[0-2]|[0-4][0-9])$:regex|required|integer') .addValidator('input5', 'email|required|min_len:6') @@ -88,11 +97,19 @@ myApp.controller('CtrlValidationService', ['$scope', '$translate', 'validationSe .addValidator('input19', 'date_us_short_between:11/28/99,12/31/15|required') .addValidator('area1', 'alpha_dash_spaces|min_len:15|required'); - + // remove a single element (string) OR you can also remove multiple elements through an array type .removeValidator(['input2','input3']) $scope.removeInputValidator = function ( elmName ) { - // remove a single element (string) OR you can also remove multiple elements through an array type .removeValidator(['input2','input3']) - myValidation.removeValidator(elmName); - + myValidation.removeValidator(elmName); $scope.enableRemoveInputValidatorButton = false; }; + + $scope.showValidationSummary = function () { + $scope.displayValidationSummary = true; + } + + $scope.submitForm = function() { + if(myValidation.checkFormValidity($scope.form1)) { + alert('All good, proceed with submit...'); + } + } }]); \ No newline at end of file diff --git a/bower.json b/bower.json index de2918e..571a849 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ghiscoding.angular-validation", - "version": "1.3.9", + "version": "1.3.10", "authors": [ "Ghislain B." ], diff --git a/changelog.txt b/changelog.txt index c6a964f..ffa3fa9 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,4 +10,5 @@ Angular-Validation change logs 1.3.6 (2015-02-09): Added ng-strict-di for minification, renamed some files and folder lib to /vendors, moved directive into new /src folder for better separation. 1.3.7 (2015-03-08): Complete rewrite (but same functionality) so that I could add an Angular-Validation Service which is similar implementation as the Directive. Also added `debounce` attribute which is an alias to `typingLimit`, validation rules are now defined as an external service for better maintainability and also created a common file for shared functions by both Validation Directive and Service. 1.3.8 (2015-03-15): Added between/min/max conditional validators on all Date types (ISO, EURO_LONG, EURO_SHORT, US_LONG, US_SHORT) -1.3.9 (2015-03-21): Added validation summary through 2 new and equivalent properties `$scope.$validationSummary` and `$scope.formName.$validationSummary`. Also added `bower` and `gulp` support, the Gulp script gives minified files. \ No newline at end of file +1.3.9 (2015-03-21): Added validation summary through 2 new and equivalent properties `$scope.$validationSummary` and `$scope.formName.$validationSummary`. Also added `bower` and `gulp` support, the Gulp script gives minified files. +1.3.10 (2015-03-28); Added new function of `checkFormValidity()` before submitting the form. Now use only 1 minified script instead of multiples. \ No newline at end of file diff --git a/dist/angular-validation-allin1.min.js b/dist/angular-validation.min.js similarity index 66% rename from dist/angular-validation-allin1.min.js rename to dist/angular-validation.min.js index d112baf..c39ae59 100644 --- a/dist/angular-validation-allin1.min.js +++ b/dist/angular-validation.min.js @@ -1,12 +1,12 @@ /** * Angular-Validation Directive and Service (ghiscoding) - * https://github.com/ghiscoding/angular-validation + * http://github.com/ghiscoding/angular-validation * @author: Ghislain B. - * @version: 1.3.9 + * @version: 1.3.10 * @license: MIT - * @date: Sat Mar 21 2015 17:40:22 GMT-0400 (Eastern Daylight Time) + * @build: Sun Mar 29 2015 00:27:23 GMT-0400 (Eastern Daylight Time) */ angular.module("ghiscoding.validation",["pascalprecht.translate"]).directive("validation",["$timeout","validationCommon","validationRules",function(i,t){return{restrict:"A",require:"ngModel",link:function(a,e,n,l){function d(){i.cancel(r),u.updateErrorMsg(""),l.$setValidity("validation",!0),e.unbind("blur")}function o(t){return u.validate(t,!1),u.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?((u.isFieldRequired()||t)&&l.$setValidity("validation",!1),e.bind("blur",function(){return a.$evalAsync(l.$setValidity("validation",u.validate(t,!0))),t}),"SELECT"===e.prop("tagName").toUpperCase()?(l.$setValidity("validation",u.validate(t,!0)),t):("undefined"!=typeof t&&(u.updateErrorMsg(""),i.cancel(r),r=i(function(){a.$evalAsync(l.$setValidity("validation",u.validate(t,!0)))},u.typingLimit)),t)):(d(),t)}var r,u=new t(a,e,n,l);l.$parsers.unshift(o),l.$formatters.unshift(o),n.$observe("disabled",function(i){i?l.$setValidity("validation",!0):l.$setValidity("validation",u.validate(l.$viewValue,!0))})}}}]); -angular.module("ghiscoding.validation").factory("validationCommon",["$timeout","$translate","validationRules",function(t,a,e){function r(){var t=this,a={};t.validators=[],t.typingLimit=c,t.validatorAttrs.hasOwnProperty("debounce")?t.typingLimit=parseInt(t.validatorAttrs.debounce,10):t.validatorAttrs.hasOwnProperty("typingLimit")&&(t.typingLimit=parseInt(t.validatorAttrs.typingLimit,10));var r=t.validatorAttrs.hasOwnProperty("rules")?t.validatorAttrs.rules:t.validatorAttrs.validation;if(r.indexOf("regex:")>=0){var i=r.match("regex:(.*?):regex");if(i.length<2)throw'Regex validator within the validation needs to be define with an opening "regex:" and a closing ":regex", please review your validator.';var s=i[1].split(":=");a={message:s[0],pattern:s[1]},r=r.replace(i[0],"regex:")}var n=r.split("|");if(n){t.bFieldRequired=n.indexOf("required")>=0?!0:!1;for(var l=0,o=n.length;o>l;l++){var d=n[l].split(":");t.validators[l]=e.getElementValidators(d[0],d[1],a)}}return t}function i(t,a,e,r){this.scope=t,this.elm=a,this.ctrl=r,this.validatorAttrs=e,this.defineValidation()}function s(){var t=this;return t.bFieldRequired}function n(t,e,r){var i=this;if("undefined"==typeof i.elm.attr("name"))throw'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="'+i.elm.attr("ng-model")+'"';var s="undefined"!=typeof r&&r?a.instant(t):t,n=i.elm.attr("name").replace(/[|&;$%@"<>()+,\[\]\{\}]/g,""),l="undefined"==typeof e?!1:!0,o=null;if(i.validatorAttrs.hasOwnProperty("validationErrorTo")){var d=i.validatorAttrs.validationErrorTo.charAt(0),p="."===d||"#"===d?i.validatorAttrs.validationErrorTo:"#"+i.validatorAttrs.validationErrorTo;o=angular.element(document.querySelector(p))}else o=angular.element(document.querySelector(".validation-"+n));l&&!e&&(i.ctrl.$dirty||i.ctrl.$touched)?o.length>0?o.text(s):i.elm.after(''+s+""):o.text("")}function l(t,e){for(var r,i=this,s=!0,n=!0,l="",d=0,v=i.validators.length;v>d;d++){if("conditionalDate"===i.validators[d].type){if(r=new RegExp(i.validators[d].pattern,"i"),s="\\S+"!==i.validators[d].pattern||"undefined"!=typeof t&&null!==t?r.test(t):!1){var c=i.validators[d].dateType,g=p(t,c).getTime();if(2==i.validators[d].params.length){var m=p(i.validators[d].params[0],c).getTime(),f=p(i.validators[d].params[1],c).getTime(),h=u(i.validators[d].condition[0],g,m),y=u(i.validators[d].condition[1],g,f);s=h&&y?!0:!1}else{var b=p(i.validators[d].params[0],c).getTime();s=u(i.validators[d].condition,g,b)}}}else if("conditionalNumber"===i.validators[d].type)if(2==i.validators[d].params.length){var h=u(i.validators[d].condition[0],parseFloat(t),parseFloat(i.validators[d].params[0])),y=u(i.validators[d].condition[1],parseFloat(t),parseFloat(i.validators[d].params[1]));s=h&&y?!0:!1}else s=u(i.validators[d].condition,parseFloat(t),parseFloat(i.validators[d].params[0]));else if("match"===i.validators[d].type){var O=i.validators[d].params[0],R=i.scope.$eval(O);s=R===t}else i.elm.prop("disabled")?s=!0:"string"==typeof t&&""===t&&"NUMBER"===i.elm.prop("type").toUpperCase()?(l=a.instant("INVALID_KEY_CHAR"),s=!1):(r=new RegExp(i.validators[d].pattern,"i"),s="\\S+"!==i.validators[d].pattern||"undefined"!=typeof t&&null!==t?r.test(t):!1);if(!s&&(n=!1,l+=a.instant(i.validators[d].message),"undefined"!=typeof i.validators[d].params))for(var E=0,S=i.validators[d].params.length;S>E;E++)"match"===i.validators[d].type&&S>1&&0===E||(l=l.replace(":param",i.validators[d].params[E]))}return o(i,i.elm.attr("name"),a.instant(l)),e&&i.updateErrorMsg(l,n),n}function o(t,a,e){var r=d(g,"field",a);if(r>=0&&""===e)g.splice(r,1);else if(""!==e){var i={field:a,message:e};r>=0?g[r]=i:g.push(i)}t.scope.$validationSummary=g;var s=angular.element(document.querySelector("form")).attr("name");s&&(t.scope[s].$validationSummary=g)}function d(t,a,e){for(var r=0;r8?t.substring(9).split(":"):null;break;case"UK":case"EURO":case"EURO_SHORT":case"EURO-SHORT":case"EUROPE":e=t.substring(0,8),r=t.substring(2,3),i=v(e,r),o=i[0],l=i[1],n=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"US_LONG":case"US-LONG":e=t.substring(0,10),r=t.substring(2,3),i=v(e,r),l=i[0],o=i[1],n=i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"US":case"US_SHORT":case"US-SHORT":e=t.substring(0,8),r=t.substring(2,3),i=v(e,r),l=i[0],o=i[1],n=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"ISO":default:e=t.substring(0,10),r=t.substring(4,5),i=v(e,r),n=i[0],l=i[1],o=i[2],s=t.length>10?t.substring(11).split(":"):null}var d=s&&3===s.length?s[0]:0,p=s&&3===s.length?s[1]:0,u=s&&3===s.length?s[2]:0;return new Date(n,l-1,o,d,p,u)}function v(t,a){var e=[];switch(a){case"/":e=t.split("/");break;case".":e=t.split(".");break;case"-":default:e=t.split("-")}return e}function u(t,a,e){var r=!1;switch(t){case"<":r=e>a?!0:!1;break;case"<=":r=e>=a?!0:!1;break;case">":r=a>e?!0:!1;break;case">=":r=a>=e?!0:!1;break;case"!=":case"<>":r=a!=e?!0:!1;break;case"=":case"==":r=a==e?!0:!1;break;default:r=!1}return r}var c=1e3,g=[],m=function(t,a,e,r){this.timer=null,this.bFieldRequired=!1,this.validators=[],this.typingLimit=c,this.scope=t,this.elm=a,this.ctrl=r,this.validatorAttrs=e,t&&a&&e&&r&&this.defineValidation()};return m.prototype.defineValidation=r,m.prototype.isFieldRequired=s,m.prototype.initialize=i,m.prototype.updateErrorMsg=n,m.prototype.validate=l,m}]); +angular.module("ghiscoding.validation").factory("validationCommon",["$timeout","$translate","validationRules",function(t,a,e){function r(){var t=this,a={};t.validators=[],t.typingLimit=c,t.validatorAttrs.hasOwnProperty("debounce")?t.typingLimit=parseInt(t.validatorAttrs.debounce,10):t.validatorAttrs.hasOwnProperty("typingLimit")&&(t.typingLimit=parseInt(t.validatorAttrs.typingLimit,10));var r=t.validatorAttrs.hasOwnProperty("rules")?t.validatorAttrs.rules:t.validatorAttrs.validation;if(r.indexOf("regex:")>=0){var i=r.match("regex:(.*?):regex");if(i.length<2)throw'Regex validator within the validation needs to be define with an opening "regex:" and a closing ":regex", please review your validator.';var s=i[1].split(":=");a={message:s[0],pattern:s[1]},r=r.replace(i[0],"regex:")}var n=r.split("|");if(n){t.bFieldRequired=n.indexOf("required")>=0?!0:!1;for(var l=0,o=n.length;o>l;l++){var d=n[l].split(":");t.validators[l]=e.getElementValidators(d[0],d[1],a)}}return t}function i(t,a,e,r){this.scope=t,this.elm=a,this.ctrl=r,this.validatorAttrs=e,this.defineValidation()}function s(){var t=this;return t.bFieldRequired}function n(t,e){var r=this,i=e&&e.elm?e.elm:r.elm,s=i&&i.attr("name")?i.attr("name"):null;if("undefined"==typeof s||null===s)throw'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="'+i.attr("ng-model")+'"';var n=e&&e.translate?a.instant(t):t,l=s.replace(/[|&;$%@"<>()+,\[\]\{\}]/g,""),o=null;if(r.validatorAttrs&&r.validatorAttrs.hasOwnProperty("validationErrorTo")){var d=r.validatorAttrs.validationErrorTo.charAt(0),v="."===d||"#"===d?r.validatorAttrs.validationErrorTo:"#"+r.validatorAttrs.validationErrorTo;o=angular.element(document.querySelector(v))}else o=angular.element(document.querySelector(".validation-"+l));var p=e&&e.submitted?e.submitted:!1;e&&!e.valid&&(p||r.ctrl.$dirty||r.ctrl.$touched)?o.length>0?o.text(n):i.after(''+n+""):o.text("")}function l(t,e){for(var r,i=this,s=!0,n=!0,l="",d=0,p=i.validators.length;p>d;d++){if("conditionalDate"===i.validators[d].type){if(r=new RegExp(i.validators[d].pattern,"i"),n="\\S+"!==i.validators[d].pattern||"undefined"!=typeof t&&null!==t?r.test(t):!1){var c=i.validators[d].dateType,g=v(t,c).getTime();if(2==i.validators[d].params.length){var m=v(i.validators[d].params[0],c).getTime(),h=v(i.validators[d].params[1],c).getTime(),f=u(i.validators[d].condition[0],g,m),b=u(i.validators[d].condition[1],g,h);n=f&&b?!0:!1}else{var y=v(i.validators[d].params[0],c).getTime();n=u(i.validators[d].condition,g,y)}}}else if("conditionalNumber"===i.validators[d].type)if(2==i.validators[d].params.length){var f=u(i.validators[d].condition[0],parseFloat(t),parseFloat(i.validators[d].params[0])),b=u(i.validators[d].condition[1],parseFloat(t),parseFloat(i.validators[d].params[1]));n=f&&b?!0:!1}else n=u(i.validators[d].condition,parseFloat(t),parseFloat(i.validators[d].params[0]));else if("match"===i.validators[d].type){var O=i.validators[d].params[0],R=i.scope.$eval(O);n=R===t}else i.elm.prop("disabled")?n=!0:"string"==typeof t&&""===t&&"NUMBER"===i.elm.prop("type").toUpperCase()?(l=a.instant("INVALID_KEY_CHAR"),n=!1):(r=new RegExp(i.validators[d].pattern,"i"),n="\\S+"!==i.validators[d].pattern||"undefined"!=typeof t&&null!==t?r.test(t):!1);if(!n&&(s=!1,l+=a.instant(i.validators[d].message),"undefined"!=typeof i.validators[d].params))for(var A=0,E=i.validators[d].params.length;E>A;A++)"match"===i.validators[d].type&&E>1&&0===A||(l=l.replace(":param",i.validators[d].params[A]))}return o(i,a.instant(l)),e&&i.updateErrorMsg(l,{valid:s}),s}function o(t,a){var e=t.elm.attr("name"),r=d(g,"field",e);if(r>=0&&""===a)g.splice(r,1);else if(""!==a){var i={field:e,message:a};r>=0?g[r]=i:g.push(i)}t.scope.$validationSummary=g;var s=angular.element(document.querySelector("form")).attr("name");s&&(t.scope[s].$validationSummary=g)}function d(t,a,e){for(var r=0;r8?t.substring(9).split(":"):null;break;case"UK":case"EURO":case"EURO_SHORT":case"EURO-SHORT":case"EUROPE":e=t.substring(0,8),r=t.substring(2,3),i=p(e,r),o=i[0],l=i[1],n=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"US_LONG":case"US-LONG":e=t.substring(0,10),r=t.substring(2,3),i=p(e,r),l=i[0],o=i[1],n=i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"US":case"US_SHORT":case"US-SHORT":e=t.substring(0,8),r=t.substring(2,3),i=p(e,r),l=i[0],o=i[1],n=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"ISO":default:e=t.substring(0,10),r=t.substring(4,5),i=p(e,r),n=i[0],l=i[1],o=i[2],s=t.length>10?t.substring(11).split(":"):null}var d=s&&3===s.length?s[0]:0,v=s&&3===s.length?s[1]:0,u=s&&3===s.length?s[2]:0;return new Date(n,l-1,o,d,v,u)}function p(t,a){var e=[];switch(a){case"/":e=t.split("/");break;case".":e=t.split(".");break;case"-":default:e=t.split("-")}return e}function u(t,a,e){var r=!1;switch(t){case"<":r=e>a?!0:!1;break;case"<=":r=e>=a?!0:!1;break;case">":r=a>e?!0:!1;break;case">=":r=a>=e?!0:!1;break;case"!=":case"<>":r=a!=e?!0:!1;break;case"=":case"==":r=a==e?!0:!1;break;default:r=!1}return r}var c=1e3,g=[],m=function(t,a,e,r){this.timer=null,this.bFieldRequired=!1,this.validators=[],this.typingLimit=c,this.scope=t,this.elm=a,this.ctrl=r,this.validatorAttrs=e,t&&a&&e&&r&&this.defineValidation()};return m.prototype.defineValidation=r,m.prototype.isFieldRequired=s,m.prototype.initialize=i,m.prototype.updateErrorMsg=n,m.prototype.validate=l,m}]); angular.module("ghiscoding.validation").factory("validationRules",[function(){function e(e,a,t){var s={};switch(e){case"alpha":s={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA",type:"regex"};break;case"alphaSpaces":case"alpha_spaces":s={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_SPACE",type:"regex"};break;case"alphaNum":case"alpha_num":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA_NUM",type:"regex"};break;case"alphaNumSpaces":case"alpha_num_spaces":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_NUM_SPACE",type:"regex"};break;case"alphaDash":case"alpha_dash":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$",message:"INVALID_ALPHA_DASH",type:"regex"};break;case"alphaDashSpaces":case"alpha_dash_spaces":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s_-])+$",message:"INVALID_ALPHA_DASH_SPACE",type:"regex"};break;case"betweenLen":case"between_len":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_len:1,5";s={pattern:"^.{"+n[0]+","+n[1]+"}$",message:"INVALID_BETWEEN_CHAR",params:[n[0],n[1]],type:"regex"};break;case"betweenNum":case"between_num":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_num:1,5";s={condition:[">=","<="],message:"INVALID_BETWEEN_NUM",params:[n[0],n[1]],type:"conditionalNumber"};break;case"creditCard":case"credit_card":s={pattern:"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11})$",message:"INVALID_CREDIT_CARD",type:"regex"};break;case"dateEuroLong":case"date_euro_long":s={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG",type:"regex"};break;case"dateEuroLongBetween":case"date_euro_long_between":case"betweenDateEuroLong":case"between_date_euro_long":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_long:01-01-1990,31-12-2015";s={condition:[">=","<="],dateType:"EURO_LONG",params:[n[0],n[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_BETWEEN",type:"conditionalDate"};break;case"dateEuroLongMax":case"date_euro_long_max":case"maxDateEuroLong":case"max_date_euro_long":s={condition:"<=",dateType:"EURO_LONG",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MAX",type:"conditionalDate"};break;case"dateEuroLongMin":case"date_euro_long_min":case"minDateEuroLong":case"min_date_euro_long":s={condition:">=",dateType:"EURO_LONG",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MIN",type:"conditionalDate"};break;case"dateEuroShort":case"date_euro_short":s={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT",type:"regex"};break;case"dateEuroShortBetween":case"date_euro_short_between":case"betweenDateEuroShort":case"between_date_euro_short":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_short:01-01-90,31-12-15";s={condition:[">=","<="],dateType:"EURO_SHORT",params:[n[0],n[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateEuroShortMax":case"date_euro_short_max":case"maxDateEuroShort":case"max_date_euro_short":s={condition:"<=",dateType:"EURO_SHORT",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MAX",type:"conditionalDate"};break;case"dateEuroShortMin":case"date_euro_short_min":case"minDateEuroShort":case"min_date_euro_short":s={condition:">=",dateType:"EURO_SHORT",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MIN",type:"conditionalDate"};break;case"dateIso":case"date_iso":s={pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO",type:"regex"};break;case"dateIsoBetween":case"date_iso_between":case"betweenDateIso":case"between_date_iso":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_iso:1990-01-01,2000-12-31";s={condition:[">=","<="],dateType:"ISO",params:[n[0],n[1]],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_BETWEEN",type:"conditionalDate"};break;case"dateIsoMax":case"date_iso_max":case"maxDateIso":case"max_date_iso":s={condition:"<=",dateType:"ISO",params:[a],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MAX",type:"conditionalDate"};break;case"dateIsoMin":case"date_iso_min":case"minDateIso":case"min_date_iso":s={condition:">=",dateType:"ISO",params:[a],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MIN",type:"conditionalDate"};break;case"dateUsLong":case"date_us_long":s={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG",type:"regex"};break;case"dateUsLongBetween":case"date_us_long_between":case"betweenDateUsLong":case"between_date_us_long":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_long:01/01/1990,12/31/2015";s={condition:[">=","<="],dateType:"US_LONG",params:[n[0],n[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_BETWEEN",type:"conditionalDate"};break;case"dateUsLongMax":case"date_us_long_max":case"maxDateUsLong":case"max_date_us_long":s={condition:"<=",dateType:"US_LONG",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MAX",type:"conditionalDate"};break;case"dateUsLongMin":case"date_us_long_min":case"minDateUsLong":case"min_date_us_long":s={condition:">=",dateType:"US_LONG",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MIN",type:"conditionalDate"};break;case"dateUsShort":case"date_us_short":s={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT",type:"regex"};break;case"dateUsShortBetween":case"date_us_short_between":case"betweenDateUsShort":case"between_date_us_short":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_short:01/01/90,12/31/15";s={condition:[">=","<="],dateType:"US_SHORT",params:[n[0],n[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateUsShortMax":case"date_us_short_max":case"maxDateUsShort":case"max_date_us_short":s={condition:"<=",dateType:"US_SHORT",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MAX",type:"conditionalDate"};break;case"dateUsShortMin":case"date_us_short_min":case"minDateUsShort":case"min_date_us_short":s={condition:">=",dateType:"US_SHORT",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MIN",type:"conditionalDate"};break;case"email":s={pattern:"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",message:"INVALID_EMAIL",type:"regex"};break;case"exactLen":case"exact_len":s={pattern:"^.{"+a+"}$",message:"INVALID_EXACT_LEN",params:[a],type:"regex"};break;case"float":s={pattern:"^\\d*\\.{1}\\d+$",message:"INVALID_FLOAT",type:"regex"};break;case"floatSigned":case"float_signed":s={pattern:"^[-+]?\\d*\\.{1}\\d+$",message:"INVALID_FLOAT_SIGNED",type:"regex"};break;case"iban":s={pattern:"[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}",message:"INVALID_IBAN",type:"regex"};break;case"int":case"integer":s={pattern:"^\\d+$",message:"INVALID_INTEGER",type:"regex"};break;case"intSigned":case"integerSigned":case"int_signed":case"integer_signed":s={pattern:"^[+-]?\\d+$",message:"INVALID_INTEGER_SIGNED",type:"regex"};break;case"ipv4":s={pattern:"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$",message:"INVALID_IPV4",type:"regex"};break;case"ipv6":s={pattern:"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$",message:"INVALID_IPV6",type:"regex"};break;case"ipv6_hex":s={pattern:"^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$",message:"INVALID_IPV6_HEX",type:"regex"};break;case"match":var _=a.split(",");s={message:"INVALID_INPUT_MATCH",params:_,type:"match"};break;case"maxLen":case"max_len":s={pattern:"^.{0,"+a+"}$",message:"INVALID_MAX_CHAR",params:[a],type:"regex"};break;case"maxNum":case"max_num":s={condition:"<=",message:"INVALID_MAX_NUM",params:[a],type:"conditionalNumber"};break;case"minLen":case"min_len":s={pattern:"^.{"+a+",}$",message:"INVALID_MIN_CHAR",params:[a],type:"regex"};break;case"minNum":case"min_num":s={condition:">=",message:"INVALID_MIN_NUM",params:[a],type:"conditionalNumber"};break;case"numeric":s={pattern:"^\\d*\\.?\\d+$",message:"INVALID_NUMERIC",type:"regex"};break;case"numericSigned":case"numeric_signed":s={pattern:"^[-+]?\\d*\\.?\\d+$",message:"INVALID_NUMERIC_SIGNED",type:"regex"};break;case"regex":s={pattern:t.pattern,message:"INVALID_PATTERN",params:[t.message],type:"regex"};break;case"required":s={pattern:"\\S+",message:"INVALID_REQUIRED",type:"regex"};break;case"url":s={pattern:"(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?",message:"INVALID_URL",type:"regex"};break;case"time":s={pattern:"^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$",message:"INVALID_TIME",type:"regex"}}return s}var a={getElementValidators:e};return a}]); -angular.module("ghiscoding.validation").service("validationService",["$timeout","validationCommon",function(e,o){function t(e,o){var t=this,n={};if("string"==typeof e&&"string"==typeof o?(n.elmName=e,n.rules=o):n=e,"object"!=typeof n||!n.hasOwnProperty("elmName")||!n.hasOwnProperty("rules")||!n.hasOwnProperty("scope")&&"undefined"==typeof t.validationAttrs.scope)throw"Angular-Validation-Service requires at least the following 3 attributes: {elmName, rules, scope}";return n.elm=angular.element(document.querySelector('[name="'+n.elmName+'"]:not([disabled]):not([ng-disabled]')),"object"!=typeof n.elm||0===n.elm.length?t:(n.elm.bind("blur",function(e){t.commonObj.initialize(n.scope,n.elm,n,n.ctrl),t.commonObj.typingLimit=0,r(t,e.target.value)}),n=l(t.validationAttrs,n),n.scope.$watch(n.elmName,function(e,o){return void 0===e&&void 0!==o?void t.commonObj.updateErrorMsg("INVALID_KEY_CHAR",!1,!0):(n.ctrl=angular.element(n.elm).controller("ngModel"),n.value=e,t.commonObj.initialize(n.scope,n.elm,n,n.ctrl),void r(t,e))},!0),t)}function n(e){var o=this;if(e instanceof Array)for(var t=0,n=e.length;n>t;t++)m(o,e[t]);else m(o,e)}function i(e){var o=this;return o.validationAttrs=e,o}function r(o,t){return o.commonObj.validate(t,!1),o.commonObj.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?((o.commonObj.isFieldRequired()||t)&&o.commonObj.ctrl.$setValidity("validation",!1),"SELECT"===o.commonObj.elm.prop("tagName").toUpperCase()?(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t):("undefined"!=typeof t&&(o.commonObj.updateErrorMsg(""),e.cancel(o.timer),o.timer=e(function(){o.commonObj.scope.$evalAsync(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)))},o.commonObj.typingLimit)),t)):(a(o),t)}function a(o){e.cancel(self.timer),o.commonObj.updateErrorMsg(""),o.commonObj.ctrl.$setValidity("validation",!0),o.commonObj.elm.unbind("blur")}function l(e,o){var t={};for(var n in e)t[n]=e[n];for(var n in o)t[n]=o[n];return t}function m(e,o){if("undefined"!=typeof e.commonObj.scope){var t=e.commonObj.scope.$watch(o,function(){});t();var n=angular.element(document.querySelector('[name="'+o+'"]'));n.unbind("blur")}}var c=function(){this.validationAttrs={},this.commonObj=new o};return c.prototype.addValidator=t,c.prototype.removeValidator=n,c.prototype.setGlobalOptions=i,c}]); \ No newline at end of file +angular.module("ghiscoding.validation").service("validationService",["$timeout","validationCommon",function(e,o){function t(e,o){var t=this,n={};if("string"==typeof e&&"string"==typeof o?(n.elmName=e,n.rules=o):n=e,"object"!=typeof n||!n.hasOwnProperty("elmName")||!n.hasOwnProperty("rules")||!n.hasOwnProperty("scope")&&"undefined"==typeof t.validationAttrs.scope)throw"Angular-Validation-Service requires at least the following 3 attributes: {elmName, rules, scope}";return n.elm=angular.element(document.querySelector('[name="'+n.elmName+'"]:not([disabled]):not([ng-disabled]')),"object"!=typeof n.elm||0===n.elm.length?t:(n.elm.bind("blur",function(e){t.commonObj.initialize(n.scope,n.elm,n,n.ctrl),t.commonObj.typingLimit=0,r(t,e.target.value)}),n=m(t.validationAttrs,n),n.scope.$watch(n.elmName,function(e,o){return void 0===e&&void 0!==o?void t.commonObj.updateErrorMsg("INVALID_KEY_CHAR",{valid:!1,translate:!0}):(n.ctrl=angular.element(n.elm).controller("ngModel"),n.value=e,t.commonObj.initialize(n.scope,n.elm,n,n.ctrl),void r(t,e))},!0),t)}function n(e){var o,t,n=this,i="",a=!0;if("undefined"==typeof e)throw"Form validaty checking requires a valid form object passed as argument";for(var r=0,l=e.$validationSummary.length;l>r;r++)a=!1,i=e.$validationSummary[r].field,t=angular.element(document.querySelector('[name="'+i+'"]:not([disabled]):not([ng-disabled]')),o=angular.element(t).controller("ngModel"),t&&t.length>0&&(o.$setTouched(),n.commonObj.updateErrorMsg(e.$validationSummary[r].message,{valid:!1,elm:t,submitted:!0}));return a}function i(e){var o=this;if(e instanceof Array)for(var t=0,n=e.length;n>t;t++)c(o,e[t]);else c(o,e)}function a(e){var o=this;return o.validationAttrs=e,o}function r(o,t){return o.commonObj.validate(t,!1),o.commonObj.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?((o.commonObj.isFieldRequired()||t)&&o.commonObj.ctrl.$setValidity("validation",!1),"SELECT"===o.commonObj.elm.prop("tagName").toUpperCase()?(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t):("undefined"!=typeof t&&(o.commonObj.updateErrorMsg(""),e.cancel(o.timer),o.timer=e(function(){o.commonObj.scope.$evalAsync(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)))},o.commonObj.typingLimit)),t)):(l(o),t)}function l(o){e.cancel(self.timer),o.commonObj.updateErrorMsg(""),o.commonObj.ctrl.$setValidity("validation",!0),o.commonObj.elm.unbind("blur")}function m(e,o){var t={};for(var n in e)t[n]=e[n];for(var n in o)t[n]=o[n];return t}function c(e,o){if("undefined"!=typeof e.commonObj.scope){var t=e.commonObj.scope.$watch(o,function(){});t();var n=angular.element(document.querySelector('[name="'+o+'"]'));n.unbind("blur")}}var d=function(){this.validationAttrs={},this.commonObj=new o};return d.prototype.addValidator=t,d.prototype.checkFormValidity=n,d.prototype.removeValidator=i,d.prototype.setGlobalOptions=a,d}]); \ No newline at end of file diff --git a/dist/validation-common.min.js b/dist/validation-common.min.js deleted file mode 100644 index 74f3e75..0000000 --- a/dist/validation-common.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Angular-Validation Directive and Service (ghiscoding) - * https://github.com/ghiscoding/angular-validation - * @author: Ghislain B. - * @version: 1.3.9 - * @license: MIT - * @date: Sat Mar 21 2015 17:40:22 GMT-0400 (Eastern Daylight Time) - */ -angular.module("ghiscoding.validation").factory("validationCommon",["$timeout","$translate","validationRules",function(t,a,e){function r(){var t=this,a={};t.validators=[],t.typingLimit=c,t.validatorAttrs.hasOwnProperty("debounce")?t.typingLimit=parseInt(t.validatorAttrs.debounce,10):t.validatorAttrs.hasOwnProperty("typingLimit")&&(t.typingLimit=parseInt(t.validatorAttrs.typingLimit,10));var r=t.validatorAttrs.hasOwnProperty("rules")?t.validatorAttrs.rules:t.validatorAttrs.validation;if(r.indexOf("regex:")>=0){var i=r.match("regex:(.*?):regex");if(i.length<2)throw'Regex validator within the validation needs to be define with an opening "regex:" and a closing ":regex", please review your validator.';var s=i[1].split(":=");a={message:s[0],pattern:s[1]},r=r.replace(i[0],"regex:")}var n=r.split("|");if(n){t.bFieldRequired=n.indexOf("required")>=0?!0:!1;for(var l=0,o=n.length;o>l;l++){var d=n[l].split(":");t.validators[l]=e.getElementValidators(d[0],d[1],a)}}return t}function i(t,a,e,r){this.scope=t,this.elm=a,this.ctrl=r,this.validatorAttrs=e,this.defineValidation()}function s(){var t=this;return t.bFieldRequired}function n(t,e,r){var i=this;if("undefined"==typeof i.elm.attr("name"))throw'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="'+i.elm.attr("ng-model")+'"';var s="undefined"!=typeof r&&r?a.instant(t):t,n=i.elm.attr("name").replace(/[|&;$%@"<>()+,\[\]\{\}]/g,""),l="undefined"==typeof e?!1:!0,o=null;if(i.validatorAttrs.hasOwnProperty("validationErrorTo")){var d=i.validatorAttrs.validationErrorTo.charAt(0),p="."===d||"#"===d?i.validatorAttrs.validationErrorTo:"#"+i.validatorAttrs.validationErrorTo;o=angular.element(document.querySelector(p))}else o=angular.element(document.querySelector(".validation-"+n));l&&!e&&(i.ctrl.$dirty||i.ctrl.$touched)?o.length>0?o.text(s):i.elm.after(''+s+""):o.text("")}function l(t,e){for(var r,i=this,s=!0,n=!0,l="",d=0,v=i.validators.length;v>d;d++){if("conditionalDate"===i.validators[d].type){if(r=new RegExp(i.validators[d].pattern,"i"),s="\\S+"!==i.validators[d].pattern||"undefined"!=typeof t&&null!==t?r.test(t):!1){var c=i.validators[d].dateType,g=p(t,c).getTime();if(2==i.validators[d].params.length){var m=p(i.validators[d].params[0],c).getTime(),f=p(i.validators[d].params[1],c).getTime(),h=u(i.validators[d].condition[0],g,m),y=u(i.validators[d].condition[1],g,f);s=h&&y?!0:!1}else{var b=p(i.validators[d].params[0],c).getTime();s=u(i.validators[d].condition,g,b)}}}else if("conditionalNumber"===i.validators[d].type)if(2==i.validators[d].params.length){var h=u(i.validators[d].condition[0],parseFloat(t),parseFloat(i.validators[d].params[0])),y=u(i.validators[d].condition[1],parseFloat(t),parseFloat(i.validators[d].params[1]));s=h&&y?!0:!1}else s=u(i.validators[d].condition,parseFloat(t),parseFloat(i.validators[d].params[0]));else if("match"===i.validators[d].type){var O=i.validators[d].params[0],R=i.scope.$eval(O);s=R===t}else i.elm.prop("disabled")?s=!0:"string"==typeof t&&""===t&&"NUMBER"===i.elm.prop("type").toUpperCase()?(l=a.instant("INVALID_KEY_CHAR"),s=!1):(r=new RegExp(i.validators[d].pattern,"i"),s="\\S+"!==i.validators[d].pattern||"undefined"!=typeof t&&null!==t?r.test(t):!1);if(!s&&(n=!1,l+=a.instant(i.validators[d].message),"undefined"!=typeof i.validators[d].params))for(var E=0,S=i.validators[d].params.length;S>E;E++)"match"===i.validators[d].type&&S>1&&0===E||(l=l.replace(":param",i.validators[d].params[E]))}return o(i,i.elm.attr("name"),a.instant(l)),e&&i.updateErrorMsg(l,n),n}function o(t,a,e){var r=d(g,"field",a);if(r>=0&&""===e)g.splice(r,1);else if(""!==e){var i={field:a,message:e};r>=0?g[r]=i:g.push(i)}t.scope.$validationSummary=g;var s=angular.element(document.querySelector("form")).attr("name");s&&(t.scope[s].$validationSummary=g)}function d(t,a,e){for(var r=0;r8?t.substring(9).split(":"):null;break;case"UK":case"EURO":case"EURO_SHORT":case"EURO-SHORT":case"EUROPE":e=t.substring(0,8),r=t.substring(2,3),i=v(e,r),o=i[0],l=i[1],n=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"US_LONG":case"US-LONG":e=t.substring(0,10),r=t.substring(2,3),i=v(e,r),l=i[0],o=i[1],n=i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"US":case"US_SHORT":case"US-SHORT":e=t.substring(0,8),r=t.substring(2,3),i=v(e,r),l=i[0],o=i[1],n=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],s=t.length>8?t.substring(9).split(":"):null;break;case"ISO":default:e=t.substring(0,10),r=t.substring(4,5),i=v(e,r),n=i[0],l=i[1],o=i[2],s=t.length>10?t.substring(11).split(":"):null}var d=s&&3===s.length?s[0]:0,p=s&&3===s.length?s[1]:0,u=s&&3===s.length?s[2]:0;return new Date(n,l-1,o,d,p,u)}function v(t,a){var e=[];switch(a){case"/":e=t.split("/");break;case".":e=t.split(".");break;case"-":default:e=t.split("-")}return e}function u(t,a,e){var r=!1;switch(t){case"<":r=e>a?!0:!1;break;case"<=":r=e>=a?!0:!1;break;case">":r=a>e?!0:!1;break;case">=":r=a>=e?!0:!1;break;case"!=":case"<>":r=a!=e?!0:!1;break;case"=":case"==":r=a==e?!0:!1;break;default:r=!1}return r}var c=1e3,g=[],m=function(t,a,e,r){this.timer=null,this.bFieldRequired=!1,this.validators=[],this.typingLimit=c,this.scope=t,this.elm=a,this.ctrl=r,this.validatorAttrs=e,t&&a&&e&&r&&this.defineValidation()};return m.prototype.defineValidation=r,m.prototype.isFieldRequired=s,m.prototype.initialize=i,m.prototype.updateErrorMsg=n,m.prototype.validate=l,m}]); \ No newline at end of file diff --git a/dist/validation-directive.min.js b/dist/validation-directive.min.js deleted file mode 100644 index e21bd16..0000000 --- a/dist/validation-directive.min.js +++ /dev/null @@ -1 +0,0 @@ -angular.module("ghiscoding.validation",["pascalprecht.translate"]).directive("validation",["$timeout","validationCommon","validationRules",function(i,t){return{restrict:"A",require:"ngModel",link:function(a,e,n,l){function d(){i.cancel(r),u.updateErrorMsg(""),l.$setValidity("validation",!0),e.unbind("blur")}function o(t){return u.validate(t,!1),u.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?((u.isFieldRequired()||t)&&l.$setValidity("validation",!1),e.bind("blur",function(){return a.$evalAsync(l.$setValidity("validation",u.validate(t,!0))),t}),"SELECT"===e.prop("tagName").toUpperCase()?(l.$setValidity("validation",u.validate(t,!0)),t):("undefined"!=typeof t&&(u.updateErrorMsg(""),i.cancel(r),r=i(function(){a.$evalAsync(l.$setValidity("validation",u.validate(t,!0)))},u.typingLimit)),t)):(d(),t)}var r,u=new t(a,e,n,l);l.$parsers.unshift(o),l.$formatters.unshift(o),n.$observe("disabled",function(i){i?l.$setValidity("validation",!0):l.$setValidity("validation",u.validate(l.$viewValue,!0))})}}}]); \ No newline at end of file diff --git a/dist/validation-rules.min.js b/dist/validation-rules.min.js deleted file mode 100644 index ff16c97..0000000 --- a/dist/validation-rules.min.js +++ /dev/null @@ -1 +0,0 @@ -angular.module("ghiscoding.validation").factory("validationRules",[function(){function e(e,a,t){var s={};switch(e){case"alpha":s={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA",type:"regex"};break;case"alphaSpaces":case"alpha_spaces":s={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_SPACE",type:"regex"};break;case"alphaNum":case"alpha_num":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA_NUM",type:"regex"};break;case"alphaNumSpaces":case"alpha_num_spaces":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_NUM_SPACE",type:"regex"};break;case"alphaDash":case"alpha_dash":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$",message:"INVALID_ALPHA_DASH",type:"regex"};break;case"alphaDashSpaces":case"alpha_dash_spaces":s={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s_-])+$",message:"INVALID_ALPHA_DASH_SPACE",type:"regex"};break;case"betweenLen":case"between_len":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_len:1,5";s={pattern:"^.{"+n[0]+","+n[1]+"}$",message:"INVALID_BETWEEN_CHAR",params:[n[0],n[1]],type:"regex"};break;case"betweenNum":case"between_num":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_num:1,5";s={condition:[">=","<="],message:"INVALID_BETWEEN_NUM",params:[n[0],n[1]],type:"conditionalNumber"};break;case"creditCard":case"credit_card":s={pattern:"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11})$",message:"INVALID_CREDIT_CARD",type:"regex"};break;case"dateEuroLong":case"date_euro_long":s={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG",type:"regex"};break;case"dateEuroLongBetween":case"date_euro_long_between":case"betweenDateEuroLong":case"between_date_euro_long":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_long:01-01-1990,31-12-2015";s={condition:[">=","<="],dateType:"EURO_LONG",params:[n[0],n[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_BETWEEN",type:"conditionalDate"};break;case"dateEuroLongMax":case"date_euro_long_max":case"maxDateEuroLong":case"max_date_euro_long":s={condition:"<=",dateType:"EURO_LONG",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MAX",type:"conditionalDate"};break;case"dateEuroLongMin":case"date_euro_long_min":case"minDateEuroLong":case"min_date_euro_long":s={condition:">=",dateType:"EURO_LONG",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MIN",type:"conditionalDate"};break;case"dateEuroShort":case"date_euro_short":s={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT",type:"regex"};break;case"dateEuroShortBetween":case"date_euro_short_between":case"betweenDateEuroShort":case"between_date_euro_short":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_short:01-01-90,31-12-15";s={condition:[">=","<="],dateType:"EURO_SHORT",params:[n[0],n[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateEuroShortMax":case"date_euro_short_max":case"maxDateEuroShort":case"max_date_euro_short":s={condition:"<=",dateType:"EURO_SHORT",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MAX",type:"conditionalDate"};break;case"dateEuroShortMin":case"date_euro_short_min":case"minDateEuroShort":case"min_date_euro_short":s={condition:">=",dateType:"EURO_SHORT",params:[a],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MIN",type:"conditionalDate"};break;case"dateIso":case"date_iso":s={pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO",type:"regex"};break;case"dateIsoBetween":case"date_iso_between":case"betweenDateIso":case"between_date_iso":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_iso:1990-01-01,2000-12-31";s={condition:[">=","<="],dateType:"ISO",params:[n[0],n[1]],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_BETWEEN",type:"conditionalDate"};break;case"dateIsoMax":case"date_iso_max":case"maxDateIso":case"max_date_iso":s={condition:"<=",dateType:"ISO",params:[a],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MAX",type:"conditionalDate"};break;case"dateIsoMin":case"date_iso_min":case"minDateIso":case"min_date_iso":s={condition:">=",dateType:"ISO",params:[a],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MIN",type:"conditionalDate"};break;case"dateUsLong":case"date_us_long":s={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG",type:"regex"};break;case"dateUsLongBetween":case"date_us_long_between":case"betweenDateUsLong":case"between_date_us_long":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_long:01/01/1990,12/31/2015";s={condition:[">=","<="],dateType:"US_LONG",params:[n[0],n[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_BETWEEN",type:"conditionalDate"};break;case"dateUsLongMax":case"date_us_long_max":case"maxDateUsLong":case"max_date_us_long":s={condition:"<=",dateType:"US_LONG",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MAX",type:"conditionalDate"};break;case"dateUsLongMin":case"date_us_long_min":case"minDateUsLong":case"min_date_us_long":s={condition:">=",dateType:"US_LONG",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MIN",type:"conditionalDate"};break;case"dateUsShort":case"date_us_short":s={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT",type:"regex"};break;case"dateUsShortBetween":case"date_us_short_between":case"betweenDateUsShort":case"between_date_us_short":var n=a.split(",");if(2!==n.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_short:01/01/90,12/31/15";s={condition:[">=","<="],dateType:"US_SHORT",params:[n[0],n[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateUsShortMax":case"date_us_short_max":case"maxDateUsShort":case"max_date_us_short":s={condition:"<=",dateType:"US_SHORT",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MAX",type:"conditionalDate"};break;case"dateUsShortMin":case"date_us_short_min":case"minDateUsShort":case"min_date_us_short":s={condition:">=",dateType:"US_SHORT",params:[a],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MIN",type:"conditionalDate"};break;case"email":s={pattern:"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",message:"INVALID_EMAIL",type:"regex"};break;case"exactLen":case"exact_len":s={pattern:"^.{"+a+"}$",message:"INVALID_EXACT_LEN",params:[a],type:"regex"};break;case"float":s={pattern:"^\\d*\\.{1}\\d+$",message:"INVALID_FLOAT",type:"regex"};break;case"floatSigned":case"float_signed":s={pattern:"^[-+]?\\d*\\.{1}\\d+$",message:"INVALID_FLOAT_SIGNED",type:"regex"};break;case"iban":s={pattern:"[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}",message:"INVALID_IBAN",type:"regex"};break;case"int":case"integer":s={pattern:"^\\d+$",message:"INVALID_INTEGER",type:"regex"};break;case"intSigned":case"integerSigned":case"int_signed":case"integer_signed":s={pattern:"^[+-]?\\d+$",message:"INVALID_INTEGER_SIGNED",type:"regex"};break;case"ipv4":s={pattern:"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$",message:"INVALID_IPV4",type:"regex"};break;case"ipv6":s={pattern:"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$",message:"INVALID_IPV6",type:"regex"};break;case"ipv6_hex":s={pattern:"^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$",message:"INVALID_IPV6_HEX",type:"regex"};break;case"match":var _=a.split(",");s={message:"INVALID_INPUT_MATCH",params:_,type:"match"};break;case"maxLen":case"max_len":s={pattern:"^.{0,"+a+"}$",message:"INVALID_MAX_CHAR",params:[a],type:"regex"};break;case"maxNum":case"max_num":s={condition:"<=",message:"INVALID_MAX_NUM",params:[a],type:"conditionalNumber"};break;case"minLen":case"min_len":s={pattern:"^.{"+a+",}$",message:"INVALID_MIN_CHAR",params:[a],type:"regex"};break;case"minNum":case"min_num":s={condition:">=",message:"INVALID_MIN_NUM",params:[a],type:"conditionalNumber"};break;case"numeric":s={pattern:"^\\d*\\.?\\d+$",message:"INVALID_NUMERIC",type:"regex"};break;case"numericSigned":case"numeric_signed":s={pattern:"^[-+]?\\d*\\.?\\d+$",message:"INVALID_NUMERIC_SIGNED",type:"regex"};break;case"regex":s={pattern:t.pattern,message:"INVALID_PATTERN",params:[t.message],type:"regex"};break;case"required":s={pattern:"\\S+",message:"INVALID_REQUIRED",type:"regex"};break;case"url":s={pattern:"(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?",message:"INVALID_URL",type:"regex"};break;case"time":s={pattern:"^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$",message:"INVALID_TIME",type:"regex"}}return s}var a={getElementValidators:e};return a}]); \ No newline at end of file diff --git a/dist/validation-service.min.js b/dist/validation-service.min.js deleted file mode 100644 index c032cbd..0000000 --- a/dist/validation-service.min.js +++ /dev/null @@ -1 +0,0 @@ -angular.module("ghiscoding.validation").service("validationService",["$timeout","validationCommon",function(e,o){function t(e,o){var t=this,n={};if("string"==typeof e&&"string"==typeof o?(n.elmName=e,n.rules=o):n=e,"object"!=typeof n||!n.hasOwnProperty("elmName")||!n.hasOwnProperty("rules")||!n.hasOwnProperty("scope")&&"undefined"==typeof t.validationAttrs.scope)throw"Angular-Validation-Service requires at least the following 3 attributes: {elmName, rules, scope}";return n.elm=angular.element(document.querySelector('[name="'+n.elmName+'"]:not([disabled]):not([ng-disabled]')),"object"!=typeof n.elm||0===n.elm.length?t:(n.elm.bind("blur",function(e){t.commonObj.initialize(n.scope,n.elm,n,n.ctrl),t.commonObj.typingLimit=0,r(t,e.target.value)}),n=l(t.validationAttrs,n),n.scope.$watch(n.elmName,function(e,o){return void 0===e&&void 0!==o?void t.commonObj.updateErrorMsg("INVALID_KEY_CHAR",!1,!0):(n.ctrl=angular.element(n.elm).controller("ngModel"),n.value=e,t.commonObj.initialize(n.scope,n.elm,n,n.ctrl),void r(t,e))},!0),t)}function n(e){var o=this;if(e instanceof Array)for(var t=0,n=e.length;n>t;t++)m(o,e[t]);else m(o,e)}function i(e){var o=this;return o.validationAttrs=e,o}function r(o,t){return o.commonObj.validate(t,!1),o.commonObj.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?((o.commonObj.isFieldRequired()||t)&&o.commonObj.ctrl.$setValidity("validation",!1),"SELECT"===o.commonObj.elm.prop("tagName").toUpperCase()?(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t):("undefined"!=typeof t&&(o.commonObj.updateErrorMsg(""),e.cancel(o.timer),o.timer=e(function(){o.commonObj.scope.$evalAsync(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)))},o.commonObj.typingLimit)),t)):(a(o),t)}function a(o){e.cancel(self.timer),o.commonObj.updateErrorMsg(""),o.commonObj.ctrl.$setValidity("validation",!0),o.commonObj.elm.unbind("blur")}function l(e,o){var t={};for(var n in e)t[n]=e[n];for(var n in o)t[n]=o[n];return t}function m(e,o){if("undefined"!=typeof e.commonObj.scope){var t=e.commonObj.scope.$watch(o,function(){});t();var n=angular.element(document.querySelector('[name="'+o+'"]'));n.unbind("blur")}}var c=function(){this.validationAttrs={},this.commonObj=new o};return c.prototype.addValidator=t,c.prototype.removeValidator=n,c.prototype.setGlobalOptions=i,c}]); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 59d6a3a..a2af99e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,71 +1,50 @@ var gulp = require('gulp'), + del = require('del'), concat = require('gulp-concat'), + deporder = require('gulp-deporder'), header = require('gulp-header'), - uglify = require('gulp-uglify'); + stripdebug = require('gulp-strip-debug'), + uglify = require('gulp-uglify'), + pkg = require('./package.json'); -var jsFiles = [ - '!src/*Spec.js', // Exclude test files - 'src/validation-directive.js', - 'src/validation-common.js', - 'src/validation-rules.js', - 'src/validation-service.js', -]; -var d = new Date(); -var headerComment = [ +var + src = 'src/', + dest = 'dist', + + d = new Date(), + jsHeaderComment = [ '/**', - ' * Angular-Validation Directive and Service (ghiscoding)', - ' * https://github.com/ghiscoding/angular-validation', - ' * @author: Ghislain B.', - ' * @version: 1.3.9', - ' * @license: MIT', - ' * @date: ' + d, + ' * ' + pkg.description, + ' * ' + pkg.homepage, + ' * @author: ' + pkg.author, + ' * @version: ' + pkg.version, + ' * @license: ' + pkg.license, + ' * @build: ' + d, ' */', ''].join('\n'); //-- Register tasks //------------------ -// min-concat all src/*.js into 1 single minified file -gulp.task('min-concat', function() { - return gulp.src(jsFiles) - .pipe(uglify()) - .pipe(concat('angular-validation-allin1.min.js')) - .pipe(gulp.dest('dist')); -}); - -// minify/uglify then concat all source files -gulp.task('min-concat-all', function() { - // src/*.js into 1 single minified file - gulp.src(jsFiles) - .pipe(uglify()) - .pipe(concat('angular-validation-allin1.min.js')) - .pipe(header(headerComment)) - .pipe(gulp.dest('dist')); +// default gulp +gulp.task('default', ['compress'], function() { - // minify the validation-common - gulp.src('src/validation-common.js') - .pipe(uglify()) - .pipe(concat('validation-common.min.js')) - .pipe(header(headerComment)) - .pipe(gulp.dest('dist')); - - // minify the validation-directive - gulp.src('src/validation-directive.js') - .pipe(uglify()) - .pipe(concat('validation-directive.min.js')) - .pipe(gulp.dest('dist')); +}); - // minify the validation-rules - gulp.src('src/validation-rules.js') - .pipe(uglify()) - .pipe(concat('validation-rules.min.js')) - .pipe(gulp.dest('dist')); +// clean the dist folder +gulp.task('clean', function() { + del([ + dest + '*' + ]) +}); - // minify the validation-service - gulp.src('src/validation-service.js') +// compress our src folder +gulp.task('compress', ['clean'], function() { + // src/*.js into 1 single minified file + gulp.src(src + '*.js') + .pipe(deporder()) .pipe(uglify()) - .pipe(concat('validation-service.min.js')) + .pipe(concat('angular-validation.min.js')) + .pipe(header(jsHeaderComment)) .pipe(gulp.dest('dist')); - - return; }); \ No newline at end of file diff --git a/index.html b/index.html index 131a3ff..0ac2849 100644 --- a/index.html +++ b/index.html @@ -2,17 +2,17 @@ - Angular-Validation (ghiscoding) + Angular-Validation (ghiscoding) - +

Angular-Validation Directive|Service (ghiscoding)

- Ghiscoding Github Project | + Ghiscoding Github Project | Plunker Live Demo - +
{{'CHANGE_LANGUAGE' | translate}}: @@ -26,30 +26,32 @@

Angular-Validation Directive|Service (ghiscoding)

- +

- + - + - - - - - - - - + + - + + + + diff --git a/locales/validation/en.json b/locales/validation/en.json index 853080f..619b43f 100644 --- a/locales/validation/en.json +++ b/locales/validation/en.json @@ -36,10 +36,10 @@ "INVALID_IBAN": "Must a valid IBAN. ", "INVALID_INPUT_MATCH": "Confirmation field does not match specified field \":param\". ", "INVALID_INTEGER": "Must be a positive integer. ", - "INVALID_INTEGER_SIGNED": "Must be a positive or negative integer. ", - "INVALID_IPV4": "Must be a valid IP (IPV4). ", - "INVALID_IPV6": "Must be a valid IP (IPV6). ", - "INVALID_IPV6_HEX": "Must be a valid IP (IPV6 Hex). ", + "INVALID_INTEGER_SIGNED": "Must be a positive or negative integer. ", + "INVALID_IPV4": "Must be a valid IP (IPV4). ", + "INVALID_IPV6": "Must be a valid IP (IPV6). ", + "INVALID_IPV6_HEX": "Must be a valid IP (IPV6 Hex). ", "INVALID_KEY_CHAR": "Invalid keyboard entry on a field of type \"number\". ", "INVALID_MAX_CHAR": "May not be greater than :param characters. ", "INVALID_MAX_NUM": "Needs to be a numeric value, equal to, or lower than :param. ", @@ -70,7 +70,7 @@ "INPUT13": "AlphaDashSpaces + Required + Minimum(5) Characters -- MUST USE: validation-error-to=\" \"", "INPUT14": "Alphanumeric + Required -- NG-DISABLED", "INPUT15": "Password", - "INPUT16": "Password Confirmation", + "INPUT16": "Password Confirmation", "INPUT17": "Alphanumeric + Exactly(3) + Required -- debounce(5sec)", "INPUT18": "Date ISO (yyyy-mm-dd) -- minimum condition >= 2001-01-01 ", "INPUT19": "Date US SHORT (mm/dd/yy) -- between the dates 12/01/99 and 12/31/15", diff --git a/locales/validation/fr.json b/locales/validation/fr.json index acafe85..11b68e4 100644 --- a/locales/validation/fr.json +++ b/locales/validation/fr.json @@ -32,14 +32,14 @@ "INVALID_EMAIL": "Doit être une adresse courriel valide. ", "INVALID_EXACT_LEN": "Doit être d'une longueur fixe de :param caractères. ", "INVALID_FLOAT": "Doit être obligatoirement un nombre flottant positif (nombre entier exclu). ", - "INVALID_FLOAT_SIGNED": "Doit être obligatoirement un nombre flottant positif ou négatif (nombre entier exclu). ", + "INVALID_FLOAT_SIGNED": "Doit être obligatoirement un nombre flottant positif ou négatif (nombre entier exclu). ", "INVALID_IBAN": "Doit être un IBAN valide. ", - "INVALID_INPUT_MATCH": "Le champs de confirmation ne correspond pas au champs spécifié \":param\". ", + "INVALID_INPUT_MATCH": "Le champs de confirmation ne correspond pas au champs spécifié \":param\". ", "INVALID_INTEGER": "Doit être un nombre entier positif. ", - "INVALID_INTEGER_SIGNED": "Doit être un nombre entier positif ou négatif. ", - "INVALID_IPV4": "Doit être un IP valide (IPV4). ", - "INVALID_IPV6": "Doit être un IP valide (IPV6). ", - "INVALID_IPV6_HEX": "Doit être un IP valide (IPV6 Hex). ", + "INVALID_INTEGER_SIGNED": "Doit être un nombre entier positif ou négatif. ", + "INVALID_IPV4": "Doit être un IP valide (IPV4). ", + "INVALID_IPV6": "Doit être un IP valide (IPV6). ", + "INVALID_IPV6_HEX": "Doit être un IP valide (IPV6 Hex). ", "INVALID_KEY_CHAR": "Entrée clavier invalide sur un champs de type \"nombre\". ", "INVALID_MAX_CHAR": "Doit être plus petit que :param caractères. ", "INVALID_MAX_NUM": "Doit être une valeur numérique, égale ou inférieure à :param. ", diff --git a/package.json b/package.json new file mode 100644 index 0000000..111ed51 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "ghiscoding.angular-validation", + "version": "1.3.10", + "author": "Ghislain B.", + "description": "Angular-Validation Directive and Service (ghiscoding)", + "main": "app.js", + "dependencies": { + "gulp": "^3.8.11" + }, + "devDependencies": { + "del": "^1.1.1", + "gulp": "^3.8.11", + "gulp-concat": "^2.5.2", + "gulp-deporder": "^1.0.0", + "gulp-header": "^1.2.2", + "gulp-strip-debug": "^1.0.2", + "gulp-uglify": "^1.1.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "angular-validation" + ], + "license": "MIT", + "homepage": "http://github.com/ghiscoding/angular-validation", + "repository": { + "type": "git", + "url": "git://github.com/ghiscoding/angular-validation" + } +} diff --git a/readme.md b/readme.md index e040553..d134447 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ #Angular Validation (Directive / Service) -`Version: 1.3.9` +`Version: 1.3.10` ### Form validation after user inactivity of default 1sec. (customizable timeout) Forms Validation with Angular made easy! Angular-Validation is an angular directive/service with locales (languages) with a very simple approach of defining your `validation=""` directly within your element to validate (input, textarea, etc) and...that's it!!! The directive/service will take care of the rest! @@ -8,13 +8,13 @@ The base concept is not new, it comes from the easy form input validation approa For a smoother user experience, I also added validation on inactivity (timer/debounce). So validation will not bother the user while he is still typing... though as soon as the user pauses for a certain amount of time, then validation comes into play. It's worth knowing that this inactivity timer is only available while typing, if user focuses away from his input (onBlur) it will then validate instantly. -Supporting AngularJS 1.3.x +Supporting AngularJS 1.3.x *current code should work with 1.2.x just the same but is no more verified* Now support Service using the same functionality as the Directive. Huge rewrite to have a better code separation and also adding support to Service functionality. Specifically the `validation-rules` was separated to add rules without affecting the core while `validation-common` is for shared functions (shared by Directive/Service). -[Validation summary](#validation-summary) was also recently added to easily show all validation errors that are still active on the form. +[Validation summary](#validation-summary) was also recently added to easily show all validation errors that are still active on the form and you can also use 2 ways of dealing with the [Submit](#submit) button. ## Live Demo @@ -32,7 +32,9 @@ bower install ghiscoding.angular-validation Include it in your app project -------------------- -The validation scripts are now available in 2 formats: minified (`dist/*.js`) or uncompressed (`src/*.js`). The minified scripts are available in 4 individual scripts (same as `scr/` but minified) or as an all in 1 file that englobes them all into 1 minified file. The Directive and/or Service are totally independent and could be called together or separately BUT you will still need the `validation-rules` and `validation-common` files. Also note that `angular-translate` is also a [dependency](#dependencies). +The angular-validation scripts are now available in 2 formats: uncompressed (`src/*.js`) or minified (`dist/angular-validation.min.js`). The minified script englobes all uncompressed scripts. Also note that `angular-translate` (3rd party app) is also a [dependency](#dependencies). + +*If you use the uncompressed scripts, it is worth to know that the Directive and/or Service are totally independent and could be called together or separately BUT you will still need the `validation-rules` and `validation-common` files.* ```javascript // include it your app module ( we need both Translate & Validation) var myApp = angular.module('myApp', ['ngRoute', 'ghiscoding.validation', 'pascalprecht.translate']); @@ -52,18 +54,19 @@ myApp.config(function ($translateProvider) { ``` ```html - - - - - - - - + + + + + + + + + ``` ## Requirements -Angular-Validation requires the element which will use validation to have an html `name=""` attribute, so that in the background it can use this name to create and show an error into a `` which will directly follow your form input. For example: ``. +Angular-Validation requires the element which will use validation to have an html `name=""` attribute, so that in the background it can use this name to create and show an error into a `` which will directly follow your form input. For example: ``. *The necessity of `name=""` attribute is new since version 1.3.4+, prior to this change we were asking the user to create his own `` for error displaying. In other words, the `` is now optional, but the `name=""` attribute becomes mandatory and will throw an error if omitted* @@ -98,14 +101,14 @@ P.S. For real live sample, see the [live plunker demo](#plunker) or download the -
- + @@ -131,7 +134,7 @@ P.S. For real live sample, see the [live demo](#plunker) or download the Github // start by creating the service var myValidation = new validationService(); - // you can create indepent call to the validation service + // you can create indepent call to the validation service myValidation.addValidator({ elmName: 'input2', debounce: 3000, @@ -145,7 +148,7 @@ P.S. For real live sample, see the [live demo](#plunker) or download the Github // #1 .addValidtor('myElementName', 'myRules') ... #2 .addValidator({ elmName: 'inputX', rules: 'myRules'}) // the available object properties are the exact same set as the directive except that they are camelCase myValidation - .setGlobalOptions({ debounce: 1500, scope: $scope }) + .setGlobalOptions({ debounce: 1500, scope: $scope }) .addValidator('input3', 'float_signed|between_num:-0.6,99.5|required') .addValidator('input4', 'exact_len:4|regex:YYWW:=^(0[9]|1[0-9]|2[0-9]|3[0-9])(5[0-2]|[0-4][0-9])$:regex|required|integer') .addValidator('input5', 'email|required|min_len:6'); @@ -153,14 +156,36 @@ P.S. For real live sample, see the [live demo](#plunker) or download the Github // you can also remove a Validator with an ngClick or whichever way you prefer by calling .removeValidator() $scope.removeInputValidator = function ( elmName ) { // remove a single element (string) OR you can also remove multiple elements through an array type .removeValidator(['input2','input3']) - myValidation.removeValidator(elmName); + myValidation.removeValidator(elmName); }; ``` + +## Form Submitting and Validation +The Angular-Validation concept was first introduced with the use `ngDisabled` on the submit button, but this might not always be the best option so as a another feature we could simply leave the submit button available but run an extra function to check the form validation before proceeding with our submit (that function is available through the ValidationService). + +*You could also use the [Validation summary](#validation-summary) for displaying all errors as a header or even a footer* +```html + + + + + +``` +```javascript +// Option #2 will need to call the `checkFormValidity()` function checking the form before doing a real submit +$scope.submitForm = function() { + var myValidation = new validationService(); + if(myValidation.checkFormValidity($scope.form1)) { + // proceed with submit + } +} +``` + ## Validation Summary -Display a validation summary of all the current form errors. Validation summary can ben called through 2 properties `$scope.$validationSummary` and `$scope.formName.$validationSummary`(the latter will only works if your html Form object has a `name=""` attribute. For example `
` would then have a `$scope.form1.$validationSummary`). +Display a validation summary of all the current form errors. Validation summary can ben called through 2 properties `$scope.$validationSummary` and `$scope.formName.$validationSummary`(the latter will only works if your html Form object has a `name=""` attribute. For example `` would then have a `$scope.form1.$validationSummary`). *The code sample displayed at the bottom is only meant for showing the Validation Summary but you most probably would want to prevent the Form from being submitted when invalid or submit it when it does become valid. I will leave it up to you to code it the way you want.* @@ -170,7 +195,7 @@ Display a validation summary of all the current form errors. Validation summary
  • {{item.field }}: {{item.message}}
-
+ @@ -185,8 +210,8 @@ Well let's face it, having the `` for error display right after the elemen
@ -
@@ -212,11 +237,11 @@ Well let's face it, having the `` for error display right after the elemen Regular Expressions (Regex) -------------------- -From the example displayed, I introduce the custom regular expression, there is no limitation on regex itself and you can even use the pipe " | " within it and without being scared of interfering with the other validation filters BUT you have to follow a specific pattern (a writing pattern that is), and if you don't, well it will simply fail. Let's explain how it works... +From the example displayed, I introduce the custom regular expression, there is no limitation on regex itself and you can even use the pipe " | " within it and without being scared of interfering with the other validation filters BUT you have to follow a specific pattern (a writing pattern that is), and if you don't, well it will simply fail. Let's explain how it works... -Regex validation is divided in 4 specific parts (Step #1-4). +Regex validation is divided in 4 specific parts (Step #1-4). -Let's use the previous [Examples #5](#examples-directives) and extract the information out of it to see how it works. +Let's use the previous [Examples #5](#examples-directives) and extract the information out of it to see how it works. Step #1-4 are for explanation only, at the end we show the full regex (make sure there is no spaces). 1. Start & end the filter with the following `regex: :regex` which tells the directive where to extract it. @@ -238,15 +263,15 @@ NOTE: To be fully localized, I should add the country code at the end of my JSON ```javascript // define a key, could be on the fly with a button or a menu link -var key = 'fr'; +var key = 'fr'; // change the translation language & reload the page to make sure all errors were rendered properly -$scope.switchLanguage = function (key) { +$scope.switchLanguage = function (key) { $translate.use(key).then(function() { $route.reload(); - }); + }); }; -``` +``` *P.S. If you define a new Language set, please make a pull request and I would be happy to add them in current project... It would be nice to have Spanish, German or even Chinese :) Thank you.* @@ -333,9 +358,9 @@ Dependencies License ----- -[MIT License](http://www.opensource.org/licenses/mit-license.php) +[MIT License](http://www.opensource.org/licenses/mit-license.php) -# TODO +# TODO #### Any kind of help is welcome from the TODO list * Add more validators... * Add more locale languages... I need your help on that one!!! @@ -347,7 +372,8 @@ License * [1.3.3](https://github.com/ghiscoding/angular-validation/commit/7b3043a97006a3d7043b198f89c91f8b6c49476e) `2015-01-04` Added changelog & updated Bootstrap(3.3.1), AngularJS(1.3.7) to latest versions * [1.3.4](https://github.com/ghiscoding/angular-validation/commit/ba30d55ddb8bca44a8032fc8253356450bd4e1d4) `2015-01-06` Removed the necessity of creating a `` for displaying the error message, the directive now handles it by itself. * [1.3.5](https://github.com/ghiscoding/angular-validation/commit/679b24ca4daee8419731c45d1d65d63cb5ca74a5) `2015-01-26` Throw an error message when user did not provide a `name=""` property inside the element to validate. -* [1.3.6](https://github.com/ghiscoding/angular-validation/commit/e47e91f45f93a3f191ab6849d06163563674e9e2) `2015-02-09` Added `ng-strict-di` for minification, renamed some files and folder lib to `/vendors`, moved directive into new `/src` folder for better separation. +* [1.3.6](https://github.com/ghiscoding/angular-validation/commit/e47e91f45f93a3f191ab6849d06163563674e9e2) `2015-02-09` Added `ng-strict-di` for minification, renamed some files and folder lib to `/vendors`, moved directive into new `/src` folder for better separation. * [1.3.7](https://github.com/ghiscoding/angular-validation/commit/86c16f720d6687d3b5ca93e49a0a37824027e583) `2015-03-08` Complete rewrite (but same functionality) so that I could add an Angular-Validation Service which is similar implementation as the Directive. Also added `debounce` attribute which is an alias to `typingLimit`, validation rules are now defined as an external service for better maintainability and also created a common file for shared functions by both Validation Directive and Service. * [1.3.8](https://github.com/ghiscoding/angular-validation/commit/492d1060a91fb8b49fc70a0c7a1a581d904e0db0) `2015-03-15` Added between/min/max conditional validators on all Date types (ISO, EURO_LONG, EURO_SHORT, US_LONG, US_SHORT) -* [1.3.9](https://github.com/ghiscoding/angular-validation/commit/931d3b04a00f0583612aefe28ad0bfcac326a38c) `2015-03-21` Added validation summary through 2 new and equivalent properties `$scope.$validationSummary` and `$scope.formName.$validationSummary`. Also added `bower` and `gulp` support, the Gulp script gives minified files. \ No newline at end of file +* [1.3.9](https://github.com/ghiscoding/angular-validation/commit/931d3b04a00f0583612aefe28ad0bfcac326a38c) `2015-03-21` Added validation summary through 2 new and equivalent properties `$scope.$validationSummary` and `$scope.formName.$validationSummary`. Also added `bower` and `gulp` support, the Gulp script gives minified files. +* [1.3.10]() `2015-03-29` Added new function of `checkFormValidity()` before submitting the form. Now use only 1 minified script instead of multiples. \ No newline at end of file diff --git a/src/validation-common.js b/src/validation-common.js index 2481aee..e03a65b 100644 --- a/src/validation-common.js +++ b/src/validation-common.js @@ -1,3 +1,5 @@ +// requires: validation-directive.js + /** * angular-validation-common (ghiscoding) * https://github.com/ghiscoding/angular-validation @@ -18,7 +20,7 @@ angular var value; var timer; var typingLimit; - var TYPING_LIMIT = 1000; + var TYPING_LIMIT = 1000; var validators = []; var validatorAttrs = {}; var validationSummary = []; @@ -36,7 +38,7 @@ angular if(!!scope && !!elm && !!attrs && !!ctrl) { this.defineValidation(); - } + } }; validationCommon.prototype.defineValidation = defineValidation; @@ -52,11 +54,11 @@ angular // Public functions declaration //---------------------------------- - function defineValidation() { - var self = this; + function defineValidation() { + var self = this; var customUserRegEx = {}; self.validators = []; // reset the global validators - + // debounce (alias of typingLimit) timeout after user stop typing and validation comes in play self.typingLimit = TYPING_LIMIT; if(self.validatorAttrs.hasOwnProperty('debounce')) { @@ -64,7 +66,7 @@ angular }else if(self.validatorAttrs.hasOwnProperty('typingLimit')) { self.typingLimit = parseInt(self.validatorAttrs.typingLimit, 10); } - + // We first need to see if the validation holds a custom user regex, if it does treat it first // So why treat it separately? Because a Regex might hold pipe '|' and so we don't want to mix it with our regular validation pipe // Return string will have the complete regex pattern removed but we will keep ':regex' so that we can still loop over it @@ -80,10 +82,10 @@ angular pattern: regAttrs[1] } - // rewrite the rules so that it doesn't contain the regex: ... :regex ending + // rewrite the rules so that it doesn't contain the regex: ... :regex ending // we simply remove it so that it won't break if there's a pipe | inside the actual regex rules = rules.replace(matches[0], 'regex:'); - } + } // at this point it's safe to split with pipe (since regex was previously stripped out) var validations = rules.split('|'); @@ -110,7 +112,7 @@ angular this.defineValidation(); } - + /** @return isFieldRequired */ function isFieldRequired() { var self = this; @@ -122,24 +124,27 @@ angular * @param bool isFieldValid: is the field valid? * @param string message: error message to display */ - function updateErrorMsg(message, isFieldValid, translate) { + function updateErrorMsg(message, attrs) { var self = this; + // element name could be defined in the `attrs` or in the self object + var elm = (!!attrs && attrs.elm) ? attrs.elm : self.elm; + var elmName = (!!elm && elm.attr('name')) ? elm.attr('name') : null; + // Make sure that element has a name="" attribute else it will not work - if(typeof self.elm.attr('name') === "undefined") { - throw 'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="' + self.elm.attr('ng-model') + '"'; + if(typeof elmName === "undefined" || elmName === null) { + throw 'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="' + elm.attr('ng-model') + '"'; } // user might have passed a message to be translated - var errorMsg = (typeof translate !== "undefined" && translate) ? $translate.instant(message) : message; + var errorMsg = (!!attrs && !!attrs.translate) ? $translate.instant(message) : message; // get the name attribute of current element, make sure to strip dirty characters, for example remove a , we need to strip the "[]" - var elmInputName = self.elm.attr('name').replace(/[|&;$%@"<>()+,\[\]\{\}]/g, ''); - var hasValidation = (typeof isFieldValid === "undefined") ? false : true; + var elmInputName = elmName.replace(/[|&;$%@"<>()+,\[\]\{\}]/g, ''); var errorElm = null; // find the element which we'll display the error message, this element might be defined by the user with 'validationErrorTo' - if(self.validatorAttrs.hasOwnProperty('validationErrorTo')) { + if(!!self.validatorAttrs && self.validatorAttrs.hasOwnProperty('validationErrorTo')) { // validationErrorTo can be used in 3 different ways: with '.' (element error className) or with/without '#' (element error id) var firstChar = self.validatorAttrs.validationErrorTo.charAt(0); var selector = (firstChar === '.' || firstChar === '#') ? self.validatorAttrs.validationErrorTo : '#'+self.validatorAttrs.validationErrorTo; @@ -149,13 +154,23 @@ angular errorElm = angular.element(document.querySelector('.validation-'+elmInputName)); } - // Re-Render Error display element inside a - if(hasValidation && !isFieldValid && (self.ctrl.$dirty || self.ctrl.$touched)) { + // form might have already been submitted + var isSubmitted = (!!attrs && attrs.submitted) ? attrs.submitted : false; + /* + var formName = angular.element(document.querySelector('form')).attr('name'); + var isSubmitted = false; + if(!!formName) { + console.debug(self.scope[formName]); + isSubmitted = self.scope[formName].$submitted; + } + */ + + if(!!attrs && !attrs.valid && (isSubmitted || self.ctrl.$dirty || self.ctrl.$touched)) { // invalid & isDirty, display the error message... if not exist then create it, else udpate the text - (errorElm.length > 0) ? errorElm.text(errorMsg) : self.elm.after(''+errorMsg+''); + (errorElm.length > 0) ? errorElm.text(errorMsg) : elm.after(''+errorMsg+''); }else { // element is pristine or there's no validation applied, error message has to be blank - errorElm.text(''); + errorElm.text(''); } } @@ -166,19 +181,19 @@ angular */ function validate(strValue, showError) { var self = this; - var isValid = true; var isFieldValid = true; + var isValid = true; var message = ''; - var regex; + var regex; // loop through all validators (could be multiple) for(var j = 0, jln = self.validators.length; j < jln; j++) { - if(self.validators[j].type === "conditionalDate") { + if(self.validators[j].type === "conditionalDate") { // 1- we first need to validate that the Date input is well formed through regex // run the Regex test through each iteration, if required (\S+) and is null then it's invalid automatically regex = new RegExp(self.validators[j].pattern, 'i'); isValid = (self.validators[j].pattern === "\\S+" && (typeof strValue === "undefined" || strValue === null)) ? false : regex.test(strValue); - + // 2- date is valid, then we can do our conditional date check if(isValid) { // For Date comparison, we will need to construct a Date Object that follows the ECMA so then it could work in all browser @@ -202,10 +217,10 @@ angular } } // it might be a conditional number checking - else if(self.validators[j].type === "conditionalNumber") { + else if(self.validators[j].type === "conditionalNumber") { // if 2 params, then it's a between condition if(self.validators[j].params.length == 2) { - // this is typically a "between" condition, a range of number >= and <= + // this is typically a "between" condition, a range of number >= and <= var isValid1 = testCondition(self.validators[j].condition[0], parseFloat(strValue), parseFloat(self.validators[j].params[0])); var isValid2 = testCondition(self.validators[j].condition[1], parseFloat(strValue), parseFloat(self.validators[j].params[1])); isValid = (isValid1 && isValid2) ? true : false; @@ -218,7 +233,7 @@ angular else if(self.validators[j].type === "match") { // get the element 'value' ngModel to compare to (passed as params[0], via an $eval('ng-model="modelToCompareName"') var otherNgModel = self.validators[j].params[0]; - var otherNgModelVal = self.scope.$eval(otherNgModel); + var otherNgModelVal = self.scope.$eval(otherNgModel); isValid = (otherNgModelVal === strValue); } // or finally it might be a regular regex pattern checking @@ -231,7 +246,7 @@ angular if(typeof strValue === "string" && strValue === "" && self.elm.prop('type').toUpperCase() === "NUMBER") { message = $translate.instant("INVALID_KEY_CHAR"); isValid = false; - }else { + }else { // run the Regex test through each iteration, if required (\S+) and is null then it's invalid automatically regex = new RegExp(self.validators[j].pattern, 'i'); isValid = (self.validators[j].pattern === "\\S+" && (typeof strValue === "undefined" || strValue === null)) ? false : regex.test(strValue); @@ -239,30 +254,29 @@ angular } } if(!isValid) { - isFieldValid = false; + isFieldValid = false; message += $translate.instant(self.validators[j].message); - // replace any error message param(s) that were possibly passed + // replace any error message param(s) that were possibly passed if(typeof self.validators[j].params !== "undefined") { - for(var k = 0, kln = self.validators[j].params.length; k < kln; k++) { + for(var k = 0, kln = self.validators[j].params.length; k < kln; k++) { if(self.validators[j].type === "match" && kln > 1 && k === 0) { // if validation type is "match" and includes more than 1 param, our real text is in param[1], so we need to skip index[0] continue; } message = message.replace((':param'), self.validators[j].params[k]); - } + } } } // end !isValid } // end for() loop - // log the invalid message - addToValidationSummary(self, self.elm.attr('name'), $translate.instant(message)); + // log the invalid message + addToValidationSummary(self, $translate.instant(message)); // -- Error Display --// if(showError) { - self.updateErrorMsg(message, isFieldValid); + self.updateErrorMsg(message, {valid: isFieldValid}); } - return isFieldValid; } // validate() @@ -270,26 +284,27 @@ angular // Private functions declaration //---------------------------------- - /** Add the error to the validation summary + /** Add the error to the validation summary * @param self * @param string elmName: element name (name attribute) * @param string message: error message */ - function addToValidationSummary(self, elmName, message) { + function addToValidationSummary(self, message) { + var elmName = self.elm.attr('name'); var index = indexOfObjectInArray(validationSummary, 'field', elmName); // find index of error in our array // if message is empty, remove it from the validation summary if(index >= 0 && message === '') { - validationSummary.splice(index, 1); - }else if(message !== ''){ - var errorObj = { field: elmName, message: message}; - + validationSummary.splice(index, 1); + }else if(message !== '') { + var errorObj = { field: elmName, message: message }; + // if error already exist then refresh the error object inside the array, else push it to the array if(index >= 0) { validationSummary[index] = errorObj; }else { validationSummary.push(errorObj); - } + } } // save validation summary 2 variable locations, inside the scope object and also in the form object (if found) @@ -300,7 +315,7 @@ angular } } - /** Quick function to find an object inside an array by it's given field name and value, return the index found or -1 + /** Quick function to find an object inside an array by it's given field name and value, return the index found or -1 * @param Array sourceArray * @param string searchId: search property id * @param string searchValue: value to search @@ -323,18 +338,18 @@ angular function parseDate(dateStr, dateType) { // variables declaration var dateSubStr = '', dateSeparator = '-', dateSplit = [], timeSplit = [], year = '', month = '', day = ''; - + // Parse using the date type user selected, (separator could be dot, slash or dash) switch (dateType.toUpperCase()) { case 'EURO_LONG': - case 'EURO-LONG': // UK, Europe long format is: dd/mm/yyyy hh:mm:ss + case 'EURO-LONG': // UK, Europe long format is: dd/mm/yyyy hh:mm:ss dateSubStr = dateStr.substring(0, 10); dateSeparator = dateStr.substring(2, 3); dateSplit = splitDateString(dateSubStr, dateSeparator); day = dateSplit[0]; month = dateSplit[1]; year = dateSplit[2]; - timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; + timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; break; case 'UK': case 'EURO': @@ -347,28 +362,28 @@ angular day = dateSplit[0]; month = dateSplit[1]; year = (parseInt(dateSplit[2]) < 50) ? ('20' + dateSplit[2]) : ('19' + dateSplit[2]); // below 50 we'll consider that as century 2000's, else in century 1900's - timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; + timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; break; case 'US_LONG': case 'US-LONG': // US long format is: mm/dd/yyyy hh:mm:ss dateSubStr = dateStr.substring(0, 10); dateSeparator = dateStr.substring(2, 3); dateSplit = splitDateString(dateSubStr, dateSeparator); - month = dateSplit[0]; + month = dateSplit[0]; day = dateSplit[1]; year = dateSplit[2]; - timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; + timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; break; case 'US': case 'US_SHORT': - case 'US-SHORT': // US short format is: mm/dd/yy hh:mm:ss OR + case 'US-SHORT': // US short format is: mm/dd/yy hh:mm:ss OR dateSubStr = dateStr.substring(0, 8); dateSeparator = dateStr.substring(2, 3); dateSplit = splitDateString(dateSubStr, dateSeparator); - month = dateSplit[0]; + month = dateSplit[0]; day = dateSplit[1]; year = (parseInt(dateSplit[2]) < 50) ? ('20' + dateSplit[2]) : ('19' + dateSplit[2]); // below 50 we'll consider that as century 2000's, else in century 1900's - timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; + timeSplit = (dateStr.length > 8) ? dateStr.substring(9).split(':') : null; break; case 'ISO': default: // ISO format is: yyyy-mm-dd hh:mm:ss (separator could be dot, slash or dash: ".", "/", "-") diff --git a/src/validation-directive.js b/src/validation-directive.js index 9a7e5f8..fe58bbe 100644 --- a/src/validation-directive.js +++ b/src/validation-directive.js @@ -3,12 +3,10 @@ * https://github.com/ghiscoding/angular-validation * * @author: Ghislain B. - * @start-date: 2014-02-04 - * @last-update: 2015-02-14 - * @last-version: 1.3.7 + * @started: 2014-02-04 * * @desc: If a field becomes invalid, the text inside the error or
will show up because the error string gets filled - * Though when the field becomes valid then the error message becomes an empty string, + * Though when the field becomes valid then the error message becomes an empty string, * it will be transparent to the user even though the still exist but becomes invisible since the text is empty. * */ @@ -53,7 +51,7 @@ function cancelValidation() { $timeout.cancel(timer); commonObj.updateErrorMsg(''); - ctrl.$setValidity('validation', true); + ctrl.$setValidity('validation', true); elm.unbind('blur'); // unbind onBlur event, if not it will fail when input become dirty & empty } @@ -61,7 +59,7 @@ * and is also customizable through the (typing-limit) for which inactivity this.timer will trigger validation. * @param string value: value of the input field */ - function attemptToValidate(value) { + function attemptToValidate(value) { // pre-validate without any events just to pre-fill our validationSummary with all field errors // passing false as 2nd argument for not showing any errors on screen commonObj.validate(value, false); @@ -72,14 +70,14 @@ return value; } - // invalidate field before doing any validation - if(commonObj.isFieldRequired() || !!value) { + // invalidate field before doing any validation + if(commonObj.isFieldRequired() || !!value) { ctrl.$setValidity('validation', false); } // if field is not required and his value is empty, cancel validation and exit out // onBlur make validation without waiting - elm.bind('blur', function() { + elm.bind('blur', function() { // make the regular validation of the field value scope.$evalAsync(ctrl.$setValidity('validation', commonObj.validate(value, true) )); return value; @@ -97,13 +95,13 @@ // Make the validation only after the user has stopped activity on a field // everytime a new character is typed, it will cancel/restart the timer & we'll erase any error mmsg commonObj.updateErrorMsg(''); - $timeout.cancel(timer); - timer = $timeout(function() { + $timeout.cancel(timer); + timer = $timeout(function() { scope.$evalAsync(ctrl.$setValidity('validation', commonObj.validate(value, true) )); }, commonObj.typingLimit); } - return value; + return value; } // attemptToValidate() } // link() }; // return; diff --git a/src/validation-rules.js b/src/validation-rules.js index 43717db..c13826d 100644 --- a/src/validation-rules.js +++ b/src/validation-rules.js @@ -1,3 +1,5 @@ +// requires: validation-common.js + /** * angular-validation-rules (ghiscoding) * https://github.com/ghiscoding/angular-validation @@ -23,7 +25,7 @@ angular // Functions declaration //---------------------------------- - /** Get the element active validators and store it inside an array which will be returned + /** Get the element active validators and store it inside an array which will be returned * @param string rule: rule name * @param string ruleParam: rule extra parameter * @param object customUserRegEx: custom Regex defined by the user (*optional) @@ -41,15 +43,15 @@ angular type: "regex" }; break; - case "alphaSpaces" : - case "alpha_spaces" : + case "alphaSpaces" : + case "alpha_spaces" : validator = { pattern: "^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$", message: "INVALID_ALPHA_SPACE", type: "regex" }; break; - case "alphaNum" : + case "alphaNum" : case "alpha_num" : validator = { pattern: "^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$", @@ -92,7 +94,7 @@ angular message: "INVALID_BETWEEN_CHAR", params: [range[0], range[1]], type: "regex" - }; + }; break; case "betweenNum" : case "between_num" : @@ -122,15 +124,15 @@ angular message: "INVALID_DATE_EURO_LONG", type: "regex" }; - break; + break; case "dateEuroLongBetween" : case "date_euro_long_between" : case "betweenDateEuroLong" : - case "between_date_euro_long" : - var range = ruleParam.split(','); + case "between_date_euro_long" : + var range = ruleParam.split(','); if (range.length !== 2) { throw "This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_long:01-01-1990,31-12-2015"; - } + } validator = { condition: [">=","<="], dateType: "EURO_LONG", @@ -139,11 +141,11 @@ angular message: "INVALID_DATE_EURO_LONG_BETWEEN", type: "conditionalDate" }; - break; + break; case "dateEuroLongMax" : case "date_euro_long_max" : case "maxDateEuroLong" : - case "max_date_euro_long" : + case "max_date_euro_long" : validator = { condition: "<=", dateType: "EURO_LONG", @@ -152,11 +154,11 @@ angular message: "INVALID_DATE_EURO_LONG_MAX", type: "conditionalDate" }; - break; + break; case "dateEuroLongMin" : case "date_euro_long_min" : case "minDateEuroLong" : - case "min_date_euro_long" : + case "min_date_euro_long" : validator = { condition: ">=", dateType: "EURO_LONG", @@ -181,7 +183,7 @@ angular var range = ruleParam.split(','); if (range.length !== 2) { throw "This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_short:01-01-90,31-12-15"; - } + } validator = { condition: [">=","<="], dateType: "EURO_SHORT", @@ -190,7 +192,7 @@ angular message: "INVALID_DATE_EURO_SHORT_BETWEEN", type: "conditionalDate" }; - break; + break; case "dateEuroShortMax" : case "date_euro_short_max" : case "maxDateEuroShort" : @@ -203,7 +205,7 @@ angular message: "INVALID_DATE_EURO_SHORT_MAX", type: "conditionalDate" }; - break; + break; case "dateEuroShortMin" : case "date_euro_short_min" : case "minDateEuroShort" : @@ -232,7 +234,7 @@ angular var range = ruleParam.split(','); if (range.length !== 2) { throw "This validation must include exactly 2 params separated by a comma (,) ex.: between_date_iso:1990-01-01,2000-12-31"; - } + } validator = { condition: [">=","<="], dateType: "ISO", @@ -241,7 +243,7 @@ angular message: "INVALID_DATE_ISO_BETWEEN", type: "conditionalDate" }; - break; + break; case "dateIsoMax" : case "date_iso_max" : case "maxDateIso" : @@ -254,7 +256,7 @@ angular message: "INVALID_DATE_ISO_MAX", type: "conditionalDate" }; - break; + break; case "dateIsoMin" : case "date_iso_min" : case "minDateIso" : @@ -283,7 +285,7 @@ angular var range = ruleParam.split(','); if (range.length !== 2) { throw "This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_long:01/01/1990,12/31/2015"; - } + } validator = { condition: [">=","<="], dateType: "US_LONG", @@ -292,7 +294,7 @@ angular message: "INVALID_DATE_US_LONG_BETWEEN", type: "conditionalDate" }; - break; + break; case "dateUsLongMax" : case "date_us_long_max" : case "maxDateUsLong" : @@ -305,7 +307,7 @@ angular message: "INVALID_DATE_US_LONG_MAX", type: "conditionalDate" }; - break; + break; case "dateUsLongMin" : case "date_us_long_min" : case "minDateUsLong" : @@ -326,7 +328,7 @@ angular message: "INVALID_DATE_US_SHORT", type: "regex" }; - break; + break; case "dateUsShortBetween" : case "date_us_short_between" : case "betweenDateUsShort" : @@ -334,7 +336,7 @@ angular var range = ruleParam.split(','); if (range.length !== 2) { throw "This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_short:01/01/90,12/31/15"; - } + } validator = { condition: [">=","<="], dateType: "US_SHORT", @@ -343,7 +345,7 @@ angular message: "INVALID_DATE_US_SHORT_BETWEEN", type: "conditionalDate" }; - break; + break; case "dateUsShortMax" : case "date_us_short_max" : case "maxDateUsShort" : @@ -356,7 +358,7 @@ angular message: "INVALID_DATE_US_SHORT_MAX", type: "conditionalDate" }; - break; + break; case "dateUsShortMin" : case "date_us_short_min" : case "minDateUsShort" : @@ -383,9 +385,9 @@ angular pattern: "^.{" + ruleParam + "}$", message: "INVALID_EXACT_LEN", params: [ruleParam], - type: "regex" + type: "regex" }; - break; + break; case "float" : validator = { pattern: "^\\d*\\.{1}\\d+$", @@ -407,7 +409,7 @@ angular message: "INVALID_IBAN", type: "regex" }; - break; + break; case "int" : case "integer" : validator = { @@ -425,28 +427,28 @@ angular message: "INVALID_INTEGER_SIGNED", type: "regex" }; - break; + break; case "ipv4" : validator = { pattern: "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$", message: "INVALID_IPV4", type: "regex" }; - break; + break; case "ipv6" : validator = { pattern: "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$", message: "INVALID_IPV6", type: "regex" }; - break; + break; case "ipv6_hex" : validator = { pattern: "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$", message: "INVALID_IPV6_HEX", type: "regex" }; - break; + break; case "match" : var args = ruleParam.split(','); validator = { @@ -490,14 +492,14 @@ angular params: [ruleParam], type: "conditionalNumber" }; - break; + break; case "numeric" : validator = { pattern: "^\\d*\\.?\\d+$", message: "INVALID_NUMERIC", type: "regex" }; - break; + break; case "numericSigned" : case "numeric_signed" : validator = { @@ -536,7 +538,7 @@ angular type: "regex" }; break; - } // switch() + } // switch() return validator; } // getElementValidators() diff --git a/src/validation-service.js b/src/validation-service.js index b5fabb3..722c814 100644 --- a/src/validation-service.js +++ b/src/validation-service.js @@ -1,3 +1,5 @@ +// requires: validation-common.js + /** * Angular-Validation Service (ghiscoding) * https://github.com/ghiscoding/angular-validation @@ -10,7 +12,7 @@ angular .module('ghiscoding.validation') .service('validationService', ['$timeout', 'validationCommon', function ($timeout, validationCommon) { - // global variables + // global variables var validationAttrs; var commonObj; var timer; @@ -23,6 +25,7 @@ angular // attach the public functions to our service validationService.prototype.addValidator = addValidator; + validationService.prototype.checkFormValidity = checkFormValidity; validationService.prototype.removeValidator = removeValidator; validationService.prototype.setGlobalOptions = setGlobalOptions; @@ -32,13 +35,13 @@ angular // Public Functions declaration //---------------------------------- - /** Add a validator on a form element + /** Add a validator on a form element * @param object attrs: validator attributes */ function addValidator(var1, var2) { var self = this; - var attrs = {}; + if(typeof var1 === "string" && typeof var2 === "string") { attrs.elmName = var1; attrs.rules = var2; @@ -50,29 +53,29 @@ angular throw 'Angular-Validation-Service requires at least the following 3 attributes: {elmName, rules, scope}'; } - // find the DOM element & make sure it's an filled object before going further - // we will exclude disabled/ng-disabled element from being valited + // find the DOM element & make sure it's a filled object before going further + // we will exclude disabled/ng-disabled element from being validated attrs.elm = angular.element(document.querySelector('[name="'+attrs.elmName+'"]:not([disabled]):not([ng-disabled]')); if(typeof attrs.elm !== "object" || attrs.elm.length === 0) { return self; } // onBlur make validation without waiting - attrs.elm.bind('blur', function(event) { + attrs.elm.bind('blur', function(event) { // re-initialize to use current element & remove waiting time & validate - self.commonObj.initialize(attrs.scope, attrs.elm, attrs, attrs.ctrl); + self.commonObj.initialize(attrs.scope, attrs.elm, attrs, attrs.ctrl); self.commonObj.typingLimit = 0; attemptToValidate(self, event.target.value); }); // merge both attributes but 2nd object (attrs) as higher priority, so that for example debounce property inside `attrs` as higher priority over `validatorAttrs` // so the position inside the mergeObject call is very important - attrs = mergeObjects(self.validationAttrs, attrs); + attrs = mergeObjects(self.validationAttrs, attrs); // watch the element for any value change, validate it once that happen attrs.scope.$watch(attrs.elmName, function (newVal, oldVal) { if(newVal === undefined && oldVal !== undefined) { - self.commonObj.updateErrorMsg("INVALID_KEY_CHAR", false, true); + self.commonObj.updateErrorMsg("INVALID_KEY_CHAR", {valid: false, translate: true}); return; } // from the DOM element, find the Angular controller of this element & add value as well to list of attribtues @@ -86,7 +89,33 @@ angular return self; } // addValidator() - /** Remove a watcher + /** Is the Form all valid? Loop through Validation Summary to get the answer, if any errors are there then display them and return false + * @param object Angular Form Object + * @return bool isFormValid + */ + function checkFormValidity(form) { + var self = this; + var ctrl, elm, elmName = '', isValid = true; + if(typeof form === "undefined") { + throw 'Form validaty checking requires a valid form object passed as argument'; + } + + // loop through $validationSummary and display errors when found on each field + for(var i = 0, ln = form.$validationSummary.length; i < ln; i++) { + isValid = false; + elmName = form.$validationSummary[i].field; + elm = angular.element(document.querySelector('[name="'+elmName+'"]:not([disabled]):not([ng-disabled]')); + ctrl = angular.element(elm).controller('ngModel'); + + if(!!elm && elm.length > 0) { + ctrl.$setTouched(); // make the element as it was touched for CSS + self.commonObj.updateErrorMsg(form.$validationSummary[i].message, {valid: false, elm: elm, submitted: true}); + } + } + return isValid; + } + + /** Remove a watcher * @param array/string of element name(s) (name attribute) */ function removeValidator(attrs) { @@ -94,17 +123,17 @@ angular if(attrs instanceof Array) { for(var i = 0, ln = attrs.length; i < ln; i++) { - removeWatcher(self, attrs[i]); + removeWatcher(self, attrs[i]); } }else { - removeWatcher(self, attrs); - } + removeWatcher(self, attrs); + } } /** Set and initialize global options used by all validators */ function setGlobalOptions(attrs) { var self = this; - self.validationAttrs = attrs; // save in global + self.validationAttrs = attrs; // save in global return self; } @@ -117,19 +146,19 @@ angular * and is also customizable through the (typing-limit) for which inactivity this.timer will trigger validation. * @param string value: value of the input field */ - function attemptToValidate(self, value) { + function attemptToValidate(self, value) { // pre-validate without any events just to pre-fill our validationSummary with all field errors // passing false as 2nd argument for not showing any errors on screen self.commonObj.validate(value, false); - + // if field is not required and his value is empty, cancel validation and exit out if(!self.commonObj.isFieldRequired() && (value === "" || value === null || typeof value === "undefined")) { cancelValidation(self); return value; } - // invalidate field before doing any validation - if(self.commonObj.isFieldRequired() || !!value) { + // invalidate field before doing any validation + if(self.commonObj.isFieldRequired() || !!value) { self.commonObj.ctrl.$setValidity('validation', false); } @@ -145,20 +174,20 @@ angular // Make the validation only after the user has stopped activity on a field // everytime a new character is typed, it will cancel/restart the timer & we'll erase any error mmsg self.commonObj.updateErrorMsg(''); - $timeout.cancel(self.timer); - self.timer = $timeout(function() { + $timeout.cancel(self.timer); + self.timer = $timeout(function() { self.commonObj.scope.$evalAsync(self.commonObj.ctrl.$setValidity('validation', self.commonObj.validate(value, true) )); }, self.commonObj.typingLimit); } - return value; + return value; } // attemptToValidate() /** Cancel current validation test and blank any leftover error message */ function cancelValidation(obj) { $timeout.cancel(self.timer); obj.commonObj.updateErrorMsg(''); - obj.commonObj.ctrl.$setValidity('validation', true); + obj.commonObj.ctrl.$setValidity('validation', true); obj.commonObj.elm.unbind('blur'); // unbind onBlur event, if not it will fail when input become dirty & empty } @@ -175,7 +204,7 @@ angular return obj3; } - /** Remove a watcher + /** Remove a watcher * @param string elmName: element name (name attribute) */ function removeWatcher(self, elmName) { diff --git a/style.css b/style.css index 2160cc7..cd9aeb6 100644 --- a/style.css +++ b/style.css @@ -8,10 +8,13 @@ font-style: italic; } -/* invalid & dirty => red -- CSS3 only */ -input.ng-invalid.ng-dirty:not(:focus), -select.ng-invalid.ng-dirty:not(:focus), -textarea.ng-invalid.ng-dirty:not(:focus) { +/* invalid & (dirty or touched) => red -- CSS3 only */ +input.ng-invalid.ng-dirty:not(:focus), +input.ng-invalid.ng-touched:not(:focus), +select.ng-invalid.ng-dirty:not(:focus), +select.ng-invalid.ng-touched:not(:focus), +textarea.ng-invalid.ng-dirty:not(:focus), +textarea.ng-invalid.ng-touched:not(:focus) { border-color: #e74c3c; } diff --git a/templates/testingFormDirective.html b/templates/testingFormDirective.html index 13937aa..a021247 100644 --- a/templates/testingFormDirective.html +++ b/templates/testingFormDirective.html @@ -13,61 +13,61 @@

{{ 'ERRORS' | translate }}!

  • {{item.field }}: {{item.message}}
-
+ -
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
- + +
@@ -77,40 +77,41 @@

{{ 'ERRORS' | translate }}!

- -
-
+ +
+
-
+
-
+
-
+
-
+
-
+
-
+
- + +
\ No newline at end of file diff --git a/templates/testingFormService.html b/templates/testingFormService.html index ab1b677..10de109 100644 --- a/templates/testingFormService.html +++ b/templates/testingFormService.html @@ -16,61 +16,61 @@

{{ 'ERRORS' | translate }}!

  • {{item.field }}: {{item.message}}
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
- + +
@@ -79,40 +79,41 @@

{{ 'ERRORS' | translate }}!

- -
-
+ +
+
-
+
-
+
-
+
-
+
-
+
-
+
- + +
\ No newline at end of file