diff --git a/API.md b/API.md index 8034991..8c771e7 100644 --- a/API.md +++ b/API.md @@ -52,7 +52,7 @@ You can also add a `validation-group` directive to group many elements into a gr ```html - + @@ -245,14 +245,14 @@ in `getDefaultMsg()`,and finally return the HTML code ```javascript // your angular .config(['$validationProvider', function ($validationProvider) { - $validationProvider.setErrorHTML(function (msg) { + $validationProvider.setErrorHTML(function (msg, element, attrs) { // remember to return your HTML // eg: return '

' + msg + '

'; // or using filter // eg: return '

{{"' + msg + '"| lowercase}}

'; }); - $validationProvider.setSuccessHTML(function (msg) { + $validationProvider.setSuccessHTML(function (msg, element, attrs) { // eg: return '

' + msg + '

'; // or using filter // eg: return '

{{"' + msg + '"| lowercase}}

'; @@ -286,7 +286,7 @@ The built in `maxlength` and `minlength` validators use parameters to configure ``` -You can use parameters in your custom validators in the same way. +You can use parameters in your custom validators in the same way. You can access this parameter in the validation expression like so: ```html @@ -362,3 +362,48 @@ angular.module('yourApp', ['validation']) }; }]); ``` + + +### **Setup Message Element in config phase** +`WHY` +````html +
+ + +
+```` + +`HOW` + +In this case, I can use `message-id` attribute as a "get a job done" solution. +Because, If you choose this solution It will increase your effort of manually putting `message-id` and define your own Message Element. + +You can use `addMsgElement` as a better solution to centralize & automate your configurations. + +```javascript +// your module +angular.module('yourApp', ['validation']) + .config(['$validationProvider', function ($validationProvider) { + /** + * Add your Msg Element + * @param {DOMElement} element - Your input element + * @return void + */ + $validationProvider.addMsgElement = function(element) { + // Insert my own Msg Element + $(element).parent().append(''); + }; + + /** + * Function to help validator get your Msg Element + * @param {DOMElement} element - Your input element + * @return {DOMElement} + */ + $validationProvider.getMsgElement = function(element) { + return $(element).parent().children().last(); + }; + + }]); +``` diff --git a/dist/angular-validation.js b/dist/angular-validation.js index 09bf93c..2b31e76 100644 --- a/dist/angular-validation.js +++ b/dist/angular-validation.js @@ -269,6 +269,28 @@ } }; + /** + * Add Message Element in config phase + * When you need custom your messageElement + * NODE: this funtion & and `message-id` attribute, have similar purpose. + * This function will help you add your `messageElement` automatically instead of pre-defined. + * @param element + */ + this.addMsgElement = function(element) { + return element.after(''); + }; + + /** + * Add Message Element in config phase + * When you need custom your messageElement + * NODE: this funtion & and `message-id` attribute, have similar purpose. + * This function will help you add your `messageElement` automatically instead of pre-defined. + * @param element + */ + this.getMsgElement = function(element) { + return element.next(); + }; + /** * $get * @returns {{setErrorHTML: *, getErrorHTML: Function, setSuccessHTML: *, getSuccessHTML: Function, setExpression: *, getExpression: Function, setDefaultMsg: *, getDefaultMsg: Function, checkValid: Function, validate: Function, reset: Function}} @@ -293,7 +315,9 @@ validCallback: this.validCallback, invalidCallback: this.invalidCallback, resetCallback: this.resetCallback, - reset: this.reset + reset: this.reset, + addMsgElement: this.addMsgElement, + getMsgElement: this.getMsgElement }; }]; } @@ -383,12 +407,12 @@ var messageElem; if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup))); - else messageElem = element.next(); + else messageElem = $validationProvider.getMsgElement(element); if (element.attr('no-validation-message')) { messageElem.css('display', 'none'); } else if ($validationProvider.showSuccessMessage && messageToShow) { - messageElem.html('').append($compile($validationProvider.getSuccessHTML(messageToShow))(scope)); + messageElem.html('').append($compile($validationProvider.getSuccessHTML(messageToShow, element, attrs))(scope)); messageElem.css('display', ''); } else { messageElem.css('display', 'none'); @@ -421,12 +445,12 @@ var messageElem; if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup))); - else messageElem = element.next(); + else messageElem = $validationProvider.getMsgElement(element); if (element.attr('no-validation-message')) { messageElem.css('display', 'none'); } else if ($validationProvider.showErrorMessage && messageToShow) { - messageElem.html('').append($compile($validationProvider.getErrorHTML(messageToShow))(scope)); + messageElem.html('').append($compile($validationProvider.getErrorHTML(messageToShow, element, attrs))(scope)); messageElem.css('display', ''); } else { messageElem.css('display', 'none'); @@ -603,7 +627,7 @@ /** * Default Valid/Invalid Message */ - if (!(messageId || validationGroup)) element.after(''); + if (!(messageId || validationGroup)) $validationProvider.addMsgElement(element); /** * Set custom initial validity @@ -629,7 +653,7 @@ ctrl.$setValidity(ctrl.$name, undefined); ctrl.$render(); if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html(''); - else element.next().html(''); + else $validationProvider.getMsgElement(element).html(''); if ($validationProvider.resetCallback) $validationProvider.resetCallback(element); }); @@ -724,7 +748,7 @@ } else if (ctrl.$pristine) { // Don't validate form when the input is clean(pristine) if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html(''); - else element.next().html(''); + else $validationProvider.getMsgElement(element).html(''); return; } checkValidation(scope, element, attrs, ctrl, validation, value); @@ -737,7 +761,7 @@ attrs.$observe('noValidationMessage', function(value) { var el; if (messageId || validationGroup) el = angular.element(document.querySelector('#' + (messageId || validationGroup))); - else el = element.next(); + else el = $validationProvider.getMsgElement(element); if (value === 'true' || value === true) el.css('display', 'none'); else if (value === 'false' || value === false) el.css('display', 'block'); }); diff --git a/dist/angular-validation.min.js b/dist/angular-validation.min.js index 493e4ba..e713b12 100644 --- a/dist/angular-validation.min.js +++ b/dist/angular-validation.min.js @@ -1 +1 @@ -(function(){angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"])}).call(this),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){return a.constructor===Function?(f.getErrorHTML=a,f):void 0},this.getErrorHTML=function(a){return'

'+a+"

"},this.setSuccessHTML=function(a){return a.constructor===Function?(f.getSuccessHTML=a,f):void 0},this.getSuccessHTML=function(a){return'

'+a+"

"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$q"),d=a.get("$timeout"),e=a.get("$compile"),f=a.get("$parse"),g=function(a,c,d,g,h,i){var j,k=c||b.getDefaultMsg(d).success,l=f("success"),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):a.next(),a.attr("no-validation-message")?j.css("display","none"):b.showSuccessMessage&&k?(j.html("").append(e(b.getSuccessHTML(k))(g)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!0),l&&l({message:k}),b.validCallback&&b.validCallback(a),!0},h=function(a,c,d,g,h,i){var j,k=c||b.getDefaultMsg(d).error,l=f("error"),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):a.next(),a.attr("no-validation-message")?j.css("display","none"):b.showErrorMessage&&k?(j.html("").append(e(b.getErrorHTML(k))(g)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!1),l&&l({message:k}),b.invalidCallback&&b.invalidCallback(a),!1},i=function(a,b,c,d){var e,f=c.validationGroup,g=document.querySelectorAll("*[validation-group="+f+"]");d.$setValidity(d.$name,!1);for(var h=0,i=g.length;i>h;h++)if(e=angular.element(g[h]),e.hasClass("ng-valid")&&e[0]!==b[0])return!0;return!1},j={},k=function(a,d,e,f,j,l){var m=j.slice(0),n=m[0].trim(),o=n.indexOf("="),p=-1===o?n:n.substr(0,o),q=-1===o?null:n.substr(o+1),r=m.slice(1),s=p+"SuccessMessage",t=p+"ErrorMessage",u=b.getExpression(p),v=e.validationGroup,w={success:function(){return g(d,e[s],p,a,f,e),r.length?k(a,d,e,f,r,l):!0},error:function(){return h(d,e[t],p,a,f,e)}};return void 0===u?(console.error('You are using undefined validator "%s"',p),r.length?k(a,d,e,f,r,l):void 0):u.constructor===Function?c.all([b.getExpression(p)(l,a,d,e,q)]).then(function(b){return b&&b.length>0&&b[0]?w.success():v?void(i(a,d,e,f)||w.error()):w.error()},function(){return w.error()}):u.constructor!==RegExp?w.error():void 0!==l&&null!==l?b.getExpression(p).test(l)?w.success():w.error():v?void(i(a,d,e,f)||w.error()):w.error()},l=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},m=function(){return l()+l()+l()+l()};return{restrict:"A",require:"ngModel",link:function(a,c,e,f){var g,h=e.validator,i=e.messageId,l=e.validationGroup,n=e.validMethod,o=e.ngModel,p=function(){},q=h.split(","),r=f.validationId=m();return"boolean"==typeof a.initialValidity&&(g=a.initialValidity),i||l||c.after(""),f.$setValidity(f.$name,g),a.$on(f.$name+"reset-"+r,function(){p(),d(function(){f.$setViewValue(""),f.$setPristine(),f.$setValidity(f.$name,void 0),f.$render(),i||l?angular.element(document.querySelector("#"+(i||l))).html(""):c.next().html(""),b.resetCallback&&b.resetCallback(c)})}),n=angular.isUndefined(n)?b.getValidMethod():n,a.$on(f.$name+"submit-"+r,function(b,g){var h=f.$viewValue,i=!1;i=k(a,c,e,f,q,h),"submit"===n&&(p(),p=a.$watch(function(){return a.$eval(o)},function(b,d){b!==d&&((void 0===b||null===b)&&(b=""),i=k(a,c,e,f,q,b))}));var l=function(a){a?delete j[g]:(j[g]=c[0],d(function(){j[Math.min.apply(null,Object.keys(j))].focus()},0))};i.constructor===Object?i.then(l):l(i)}),"blur"===n?void c.bind("blur",function(){var b=a.$eval(o);a.$apply(function(){k(a,c,e,f,q,b)})}):void("submit"!==n&&"submit-only"!==n&&(a.$watch(function(){return a.$eval(o)},function(b){if(f.$pristine&&f.$viewValue)f.$setViewValue(f.$viewValue);else if(f.$pristine)return void(i||l?angular.element(document.querySelector("#"+(i||l))).html(""):c.next().html(""));k(a,c,e,f,q,b)}),d(function(){e.$observe("noValidationMessage",function(a){var b;b=i||l?angular.element(document.querySelector("#"+(i||l))):c.next(),"true"===a||a===!0?b.css("display","none"):("false"===a||a===!1)&&b.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this); \ No newline at end of file +(function(){angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"])}).call(this),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){return a.constructor===Function?(f.getErrorHTML=a,f):void 0},this.getErrorHTML=function(a){return'

'+a+"

"},this.setSuccessHTML=function(a){return a.constructor===Function?(f.getSuccessHTML=a,f):void 0},this.getSuccessHTML=function(a){return'

'+a+"

"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.addMsgElement=function(a){return a.after("")},this.getMsgElement=function(a){return a.next()},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset,addMsgElement:this.addMsgElement,getMsgElement:this.getMsgElement}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$q"),d=a.get("$timeout"),e=a.get("$compile"),f=a.get("$parse"),g=function(a,c,d,g,h,i){var j,k=c||b.getDefaultMsg(d).success,l=f("success"),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):b.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):b.showSuccessMessage&&k?(j.html("").append(e(b.getSuccessHTML(k,a,i))(g)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!0),l&&l({message:k}),b.validCallback&&b.validCallback(a),!0},h=function(a,c,d,g,h,i){var j,k=c||b.getDefaultMsg(d).error,l=f("error"),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):b.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):b.showErrorMessage&&k?(j.html("").append(e(b.getErrorHTML(k,a,i))(g)),j.css("display","")):j.css("display","none"),h.$setValidity(h.$name,!1),l&&l({message:k}),b.invalidCallback&&b.invalidCallback(a),!1},i=function(a,b,c,d){var e,f=c.validationGroup,g=document.querySelectorAll("*[validation-group="+f+"]");d.$setValidity(d.$name,!1);for(var h=0,i=g.length;i>h;h++)if(e=angular.element(g[h]),e.hasClass("ng-valid")&&e[0]!==b[0])return!0;return!1},j={},k=function(a,d,e,f,j,l){var m=j.slice(0),n=m[0].trim(),o=n.indexOf("="),p=-1===o?n:n.substr(0,o),q=-1===o?null:n.substr(o+1),r=m.slice(1),s=p+"SuccessMessage",t=p+"ErrorMessage",u=b.getExpression(p),v=e.validationGroup,w={success:function(){return g(d,e[s],p,a,f,e),r.length?k(a,d,e,f,r,l):!0},error:function(){return h(d,e[t],p,a,f,e)}};return void 0===u?(console.error('You are using undefined validator "%s"',p),r.length?k(a,d,e,f,r,l):void 0):u.constructor===Function?c.all([b.getExpression(p)(l,a,d,e,q)]).then(function(b){return b&&b.length>0&&b[0]?w.success():v?void(i(a,d,e,f)||w.error()):w.error()},function(){return w.error()}):u.constructor!==RegExp?w.error():void 0!==l&&null!==l?b.getExpression(p).test(l)?w.success():w.error():v?void(i(a,d,e,f)||w.error()):w.error()},l=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},m=function(){return l()+l()+l()+l()};return{restrict:"A",require:"ngModel",link:function(a,c,e,f){var g,h=e.validator,i=e.messageId,l=e.validationGroup,n=e.validMethod,o=e.ngModel,p=function(){},q=h.split(","),r=f.validationId=m();return"boolean"==typeof a.initialValidity&&(g=a.initialValidity),i||l||b.addMsgElement(c),f.$setValidity(f.$name,g),a.$on(f.$name+"reset-"+r,function(){p(),d(function(){f.$setViewValue(""),f.$setPristine(),f.$setValidity(f.$name,void 0),f.$render(),i||l?angular.element(document.querySelector("#"+(i||l))).html(""):b.getMsgElement(c).html(""),b.resetCallback&&b.resetCallback(c)})}),n=angular.isUndefined(n)?b.getValidMethod():n,a.$on(f.$name+"submit-"+r,function(b,g){var h=f.$viewValue,i=!1;i=k(a,c,e,f,q,h),"submit"===n&&(p(),p=a.$watch(function(){return a.$eval(o)},function(b,d){b!==d&&((void 0===b||null===b)&&(b=""),i=k(a,c,e,f,q,b))}));var l=function(a){a?delete j[g]:(j[g]=c[0],d(function(){j[Math.min.apply(null,Object.keys(j))].focus()},0))};i.constructor===Object?i.then(l):l(i)}),"blur"===n?void c.bind("blur",function(){var b=a.$eval(o);a.$apply(function(){k(a,c,e,f,q,b)})}):void("submit"!==n&&"submit-only"!==n&&(a.$watch(function(){return a.$eval(o)},function(d){if(f.$pristine&&f.$viewValue)f.$setViewValue(f.$viewValue);else if(f.$pristine)return void(i||l?angular.element(document.querySelector("#"+(i||l))).html(""):b.getMsgElement(c).html(""));k(a,c,e,f,q,d)}),d(function(){e.$observe("noValidationMessage",function(a){var d;d=i||l?angular.element(document.querySelector("#"+(i||l))):b.getMsgElement(c),"true"===a||a===!0?d.css("display","none"):("false"===a||a===!1)&&d.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this); \ No newline at end of file diff --git a/src/provider.js b/src/provider.js index 99cb6cc..6dc7cda 100644 --- a/src/provider.js +++ b/src/provider.js @@ -263,6 +263,28 @@ } }; + /** + * Add Message Element in config phase + * When you need custom your messageElement + * NODE: this funtion & and `message-id` attribute, have similar purpose. + * This function will help you add your `messageElement` automatically instead of pre-defined. + * @param element + */ + this.addMsgElement = function(element) { + return element.after(''); + }; + + /** + * Add Message Element in config phase + * When you need custom your messageElement + * NODE: this funtion & and `message-id` attribute, have similar purpose. + * This function will help you add your `messageElement` automatically instead of pre-defined. + * @param element + */ + this.getMsgElement = function(element) { + return element.next(); + }; + /** * $get * @returns {{setErrorHTML: *, getErrorHTML: Function, setSuccessHTML: *, getSuccessHTML: Function, setExpression: *, getExpression: Function, setDefaultMsg: *, getDefaultMsg: Function, checkValid: Function, validate: Function, reset: Function}} @@ -287,7 +309,9 @@ validCallback: this.validCallback, invalidCallback: this.invalidCallback, resetCallback: this.resetCallback, - reset: this.reset + reset: this.reset, + addMsgElement: this.addMsgElement, + getMsgElement: this.getMsgElement }; }]; } diff --git a/src/validator.directive.js b/src/validator.directive.js index 251838c..992f369 100644 --- a/src/validator.directive.js +++ b/src/validator.directive.js @@ -27,12 +27,12 @@ var messageElem; if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup))); - else messageElem = element.next(); + else messageElem = $validationProvider.getMsgElement(element); if (element.attr('no-validation-message')) { messageElem.css('display', 'none'); } else if ($validationProvider.showSuccessMessage && messageToShow) { - messageElem.html('').append($compile($validationProvider.getSuccessHTML(messageToShow))(scope)); + messageElem.html('').append($compile($validationProvider.getSuccessHTML(messageToShow, element, attrs))(scope)); messageElem.css('display', ''); } else { messageElem.css('display', 'none'); @@ -65,12 +65,12 @@ var messageElem; if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup))); - else messageElem = element.next(); + else messageElem = $validationProvider.getMsgElement(element); if (element.attr('no-validation-message')) { messageElem.css('display', 'none'); } else if ($validationProvider.showErrorMessage && messageToShow) { - messageElem.html('').append($compile($validationProvider.getErrorHTML(messageToShow))(scope)); + messageElem.html('').append($compile($validationProvider.getErrorHTML(messageToShow, element, attrs))(scope)); messageElem.css('display', ''); } else { messageElem.css('display', 'none'); @@ -247,7 +247,7 @@ /** * Default Valid/Invalid Message */ - if (!(messageId || validationGroup)) element.after(''); + if (!(messageId || validationGroup)) $validationProvider.addMsgElement(element); /** * Set custom initial validity @@ -273,7 +273,7 @@ ctrl.$setValidity(ctrl.$name, undefined); ctrl.$render(); if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html(''); - else element.next().html(''); + else $validationProvider.getMsgElement(element).html(''); if ($validationProvider.resetCallback) $validationProvider.resetCallback(element); }); @@ -368,7 +368,7 @@ } else if (ctrl.$pristine) { // Don't validate form when the input is clean(pristine) if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html(''); - else element.next().html(''); + else $validationProvider.getMsgElement(element).html(''); return; } checkValidation(scope, element, attrs, ctrl, validation, value); @@ -381,7 +381,7 @@ attrs.$observe('noValidationMessage', function(value) { var el; if (messageId || validationGroup) el = angular.element(document.querySelector('#' + (messageId || validationGroup))); - else el = element.next(); + else el = $validationProvider.getMsgElement(element); if (value === 'true' || value === true) el.css('display', 'none'); else if (value === 'false' || value === false) el.css('display', 'block'); }); diff --git a/test/unit/providerSpec.js b/test/unit/providerSpec.js index e41a4dc..dd977f7 100644 --- a/test/unit/providerSpec.js +++ b/test/unit/providerSpec.js @@ -228,4 +228,168 @@ describe('provider', function() { expect(validationProvider.getValidMethod()).toEqual('submit'); })); + + + + describe('validationProvider.addMsgElement', function() { + /** + * TEST CASE:Check if Default $validationProvider.addMsgElement is defined as a Function + * TEST TYPE: UNIT + * [STEP 1] SET defaultAddMsg = $validationProvider.addMsgElement + * Expected: defaultAddMsg is defined AND (defaultAddMsg instanceof Function) === true + */ + it('default value is defined as a Function', inject(function($validation) { + expect($validation.addMsgElement instanceof Function).toBe(true); + })); //END it + + + /** + * TEST CASE:Check if Default $validationProvider.addMsgElement is right after input element + * Test TYPE: E2E + * [STEP 1] SET Up + * Expected: Msg-Element is right after Input element & Total Msg-Element = 1 + */ + it('default element is right after input element', inject(function($validation) { + var msgElement = element.find('span'); + expect(msgElement.length).toEqual(1); + + var elementAfterInput = element.find('input').next(); + expect(msgElement[0].isSameNode(elementAfterInput[0])).toBe(true); + })); //END it + + + /** + * TEST CASE:Check if custom $validationProvider.addMsgElement is consistent (do not be modified/injected) + * Test TYPE: E2E + * [STEP 1] SET Up custom addMsgElement method + * [STEP 2] Place validator directive in the UI + * Expected: custom method is not be modified + */ + it('custom element is consistent', inject(function($validation) { + var customMsgElement = function(elm) {}; + $validation.addMsgElement = customMsgElement; + $compile('
')($scope); + expect($validation.addMsgElement).toEqual(customMsgElement); + })); //END it + + /** + * TEST CASE:Check if custom $validationProvider.addMsgElement is placed correctly + * Test TYPE: E2E + * [STEP 1] SET Up custom addMsgElement method to push MsgElement as the first child of "Form" element + * [STEP 2] Place validator directive in the UI + * Expected: MsgElement is placed as the first child element + */ + it('custom element is placed correctly', inject(function($validation) { + $validation.addMsgElement = function(elm) { + elm.parent().prepend('
'); + }; + var formElementForCustomMsgElm = $compile('
')($scope); + + var elementFirstChild = formElementForCustomMsgElm.children()[0]; + var elementFromSetting = formElementForCustomMsgElm.find('div')[0]; + + expect(elementFromSetting.isSameNode(elementFirstChild)).toBe(true); + })); //END it + + + /** + * TEST CASE:Check if custom $validationProvider.addMsgElement received correct parameters + * Test TYPE: E2E + * [STEP 1] SET Up custom addMsgElement + * [STEP 2] Place validator directive in the UI + * Expected: addMsgElement has 01 parameter, parameter data type = DOMElement + */ + it('received correct parameters', inject(function($validation) { + $validation.addMsgElement = function() { + expect(arguments.length).toEqual(1); + expect(angular.isElement(arguments[0])).toBe(true); + }; + $compile('
')($scope); + + })); //END it + + + }); //END describe + + describe('validationProvider.getMsgElement', function() { + /** + * TEST CASE:Check if $validationProvider.getMsgElement is defined as a Function + * TEST TYPE: UNIT + * [STEP 1] SET defaultgetMsg = $validationProvider.getMsgElement + * Expected: (getMsgElement instanceof Function) === true + */ + it('default value is defined as a Function', inject(function($validation) { + expect($validation.getMsgElement instanceof Function).toBe(true); + })); //END it + + /** + * TEST CASE:Check if default $validationProvider.getMsgElement is right after input element + * TEST TYPE: E2E + * [STEP 1] SET Up Input A: + * [STEP 2] Get Element B right after Input A + * Expected: (Element B) is (element returned from $validationProvider.getMsgElement() ); + */ + it('default element is right after input element', inject(function($validation) { + var inputElement = element.find('input'); + var elementAfterInput = inputElement.next(); + var elementFromSetting = $validation.getMsgElement(inputElement); + + expect(elementFromSetting[0].isSameNode(elementAfterInput[0])).toBe(true); + })); //END it + + + /** + * TEST CASE:Check if custom $validationProvider.getMsgElement is consistent (do not be modified/injected) + * Test TYPE: E2E + * [STEP 1] SET Up custom getMsgElement method + * [STEP 2] Place validator directive in the UI + * Expected: custom method is not be modified + */ + it('custom element is consistent', inject(function($validation) { + var customMsgElement = function(elm) {}; + $validation.getMsgElement = customMsgElement; + $compile('
')($scope); + expect($validation.getMsgElement).toEqual(customMsgElement); + })); //END it + + /** + * TEST CASE:Check if custom $validationProvider.getMsgElement is gotten correctly + * Test TYPE: E2E + * [STEP 1] SET Up custom getMsgElement method to get first child of "Form" element. + * [STEP 2] Place validator directive in the UI + * Expected: MsgElement is placed as the first child element + */ + it('custom element is placed correctly', inject(function($validation) { + $validation.getMsgElement = function(elm) { + return formElementForCustomMsgElm.children()[0]; + }; + var formElementForCustomMsgElm = $compile('
')($scope); + var inputElement = formElementForCustomMsgElm.find('input'); + + var elementFirstChild = formElementForCustomMsgElm.children()[0]; + + var elementFromSetting = $validation.getMsgElement(inputElement); + + expect(elementFromSetting.isSameNode(elementFirstChild)).toBe(true); + })); //END it + + /** + * TEST CASE:Check if custom $validationProvider.getMsgElement received correct parameters + * Test TYPE: E2E + * [STEP 1] SET Up custom getMsgElement + * [STEP 2] Place validator directive in the UI + * Expected: getMsgElement has 01 parameter, parameter data type = DOMElement + */ + it('received correct parameters', inject(function($validation) { + $validation.getMsgElement = function() { + expect(arguments.length).toEqual(1); + expect(angular.isElement(arguments[0])).toBe(true); + }; + $compile('
')($scope); + + })); //END it + + }); //END describe + + });