├── .babelrc ├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── package.json └── src └── v-validate.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-object-rest-spread" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [{package.json,*.scss,*.css}] 15 | indent_size = 2 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Crap 2 | *.log 3 | .sass-cache 4 | .Spotlight-V100 5 | .Trashes 6 | .DS_Store 7 | .DS_Store? 8 | ehthumbs.db 9 | Thumbs.db 10 | 11 | # IDE 12 | *.sublime-* 13 | *.idea 14 | .idea 15 | 16 | # Project 17 | /dist 18 | node_modules 19 | npm-debug.log 20 | yarn.lock 21 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.sublime-* 3 | __tests__ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) Appstract 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Validate 2 | 3 | [![Latest Version on NPM](https://img.shields.io/npm/v/v-validate.svg?style=flat-square)](https://npmjs.com/package/v-validate) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | 6 | Input validation for Vue 1.0 7 | 8 | [![](https://gifyu.com/images/inaction.gif)](https://gifyu.com/image/SZQV) 9 | 10 | ## Install 11 | 12 | You can install the package via yarn: 13 | 14 | ```bash 15 | $ yarn add v-validate 16 | ``` 17 | If you use webpack: 18 | ```js 19 | Vue.use(require('v-validate')); 20 | ``` 21 | If you don't, just include 'v-validate.js' somewhere in your page. 22 | 23 | ## Usage 24 | 25 | Use the `v-validate` directive on your input. 26 | Example: 27 | 28 | ```html 29 | 30 | ``` 31 | Classes `valid` or `invalid` will be appended to the input. 32 | 33 | ## See it in action 34 | 35 | Play with the options in [JSFiddle](https://jsfiddle.net/2pyx98Lr/1/) 36 | 37 | ## Contributing 38 | 39 | Contributions are welcome, [thanks to y'all](https://github.com/appstract/vue-validate/graphs/contributors) :) 40 | 41 | ## About Appstract 42 | 43 | Appstract is a small team from The Netherlands. We create (open source) tools for webdevelopment and write about related subjects on [Medium](https://medium.com/appstract). You can [follow us on Twitter](https://twitter.com/teamappstract), [buy us a beer](https://www.paypal.me/teamappstract/10) or [support us on Patreon](https://www.patreon.com/appstract). 44 | 45 | ## License 46 | 47 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v-validate", 3 | "version": "0.1.4", 4 | "description": "Input validation for Vue", 5 | "main": "src/v-validate.js", 6 | "scripts": { 7 | "lint": "eslint src" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/appstract/v-validate.git" 12 | }, 13 | "keywords": [ 14 | "appstract", 15 | "vue", 16 | "validate", 17 | "validator", 18 | "form", 19 | "input" 20 | ], 21 | "author": "Appstract", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/appstract/v-validate/issues" 25 | }, 26 | "homepage": "https://github.com/appstract/v-validate", 27 | "devDependencies": { 28 | "eslint": "^3.7.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/v-validate.js: -------------------------------------------------------------------------------- 1 | var validate = {}, 2 | errorBag = 'invalidFields', 3 | timers = {}; 4 | 5 | // exposed global options 6 | validate.config = {}; 7 | 8 | var validators = { 9 | required: function (value) { 10 | return !((value == null) || (value.length == 0)); 11 | }, 12 | 13 | numeric: function (value) { 14 | return (/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(value); 15 | }, 16 | 17 | integer: function (value) { 18 | return (/^(-?[1-9]\d*|0)$/).test(value); 19 | }, 20 | 21 | digits: function (value) { 22 | return (/^[\d() \.\:\-\+#]+$/).test(value); 23 | }, 24 | 25 | alpha: function (value) { 26 | return (/^[a-zA-Z]+$/).test(value); 27 | }, 28 | 29 | alphaNum: function (value) { 30 | return !(/\W/).test(value); 31 | }, 32 | 33 | email: function (value) { 34 | return (/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(value); 35 | }, 36 | 37 | url: function (value) { 38 | return (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(value); 39 | }, 40 | 41 | minLength: function (value, arg) { 42 | return value && value.length && value.length >= +arg; 43 | }, 44 | 45 | maxLength: function (value, arg) { 46 | return value && value.length && value.length <= +arg; 47 | }, 48 | 49 | length: function (value, arg) { 50 | return value && value.length == +arg; 51 | }, 52 | 53 | min: function (value, arg) { 54 | return value >= +arg; 55 | }, 56 | 57 | max: function (value, arg) { 58 | return value <= +arg; 59 | }, 60 | 61 | pattern: function (value, arg) { 62 | var match = arg.match(new RegExp('^/(.*?)/([gimy]*)$')); 63 | var regex = new RegExp(match[1], match[2]); 64 | return regex.test(value); 65 | } 66 | }; 67 | 68 | validate.install = function (Vue) { 69 | 70 | Vue.directive('validate', { 71 | params: ['v-model'], 72 | 73 | bind: function () { 74 | // This is the initial data 75 | this.isInitial = true; 76 | this.vm.$set('formIsValid', false); 77 | 78 | // Set the validation rules 79 | // from the expression 80 | this.validateRules = eval('('+ this.expression + ')'); 81 | 82 | // Bind the v-model to 83 | // the directive 84 | if (this.params.vModel) { 85 | this.identifier = this.params.vModel.replace(/\./g, '_'); 86 | this.expression = this.params.vModel; 87 | } else { 88 | console.error('No v-model on input'); 89 | } 90 | }, 91 | 92 | update: function (value) { 93 | if (!this.vm) { 94 | return; 95 | } 96 | 97 | if (typeof timers[this.identifier] != 'undefined') { 98 | clearTimeout(timers[this.identifier]); 99 | } 100 | 101 | if (typeof value == 'undefined') { 102 | value = ''; 103 | } 104 | 105 | var vm = this; 106 | 107 | Vue.nextTick(function () { 108 | vm.validate(value); 109 | }); 110 | }, 111 | 112 | validate: function (value) { 113 | if (!this.vm) { 114 | return; 115 | } 116 | 117 | var valid = true; 118 | var isRequired = (typeof this.validateRules['required'] != 'undefined' && this.validateRules['required']); 119 | 120 | // if empty, but not required 121 | // it is still valid 122 | if(!isRequired && value.length < 1) { 123 | 124 | } else { 125 | // validate each rule 126 | Object.keys(this.validateRules).forEach(function (rule) { 127 | if (!validators[rule]) { 128 | throw new Error('Unknown rule ' + rule); 129 | } 130 | 131 | var ruleArgument = this.validateRules[rule]; 132 | 133 | // type is boolean and it is true 134 | if (typeof(ruleArgument) == 'boolean' && ruleArgument && !validators[rule](value)) { 135 | return valid = false; 136 | } else if(!validators[rule](value, ruleArgument)) { 137 | // type has argument 138 | return valid = false; 139 | } 140 | }.bind(this)); 141 | } 142 | 143 | if (typeof this.vm[errorBag] != 'undefined') { 144 | Vue.delete(this.vm[errorBag], this.identifier); 145 | } 146 | 147 | // remove classes 148 | this.el.classList.remove('valid'); 149 | this.el.classList.remove('invalid'); 150 | 151 | if (valid) { 152 | // add the valid class 153 | if(value.length > 0) { 154 | this.el.classList.add('valid'); 155 | } 156 | } else { 157 | // add the field to the errorbag 158 | this.vm.$set(errorBag + '.' + this.identifier, true); 159 | 160 | // In case of the initial data, only add invalid class 161 | // if data is filled 162 | if (! (this.isInitial && value.length < 1) ) { 163 | var vm = this; 164 | 165 | // give a sec before adding the invalid class 166 | timers[this.identifier] = setTimeout(function(){ 167 | vm.el.classList.add('invalid'); 168 | }, 800); 169 | } 170 | } 171 | 172 | this.isInitial = false; 173 | this.setFormIsValid(); 174 | 175 | return valid; 176 | }, 177 | 178 | setFormIsValid: function () { 179 | if (typeof this.vm.$get(errorBag) == 'object' && Object.keys(this.vm.$get(errorBag)).length > 0) { 180 | this.vm.$set('formIsValid', false); 181 | } else { 182 | this.vm.$set('formIsValid', true); 183 | } 184 | } 185 | 186 | }); 187 | }; 188 | 189 | if (typeof exports == "object") { 190 | module.exports = validate; 191 | } else if (typeof define == "function" && define.amd) { 192 | define([], function(){ return validate }) 193 | } else if (window.Vue) { 194 | window.validate = validate; 195 | Vue.use(validate); 196 | } --------------------------------------------------------------------------------