qt911025 / vue-validator

Validator component for Vue.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

vue-validator

CircleCI Status Coverage Status npm package Sauce Test Status Commitizen friendly

Validator component for Vue.js

Requirements

  • Vue.js 1.0.14+

Installation

npm

stable version

$ npm install vue-validator

development version

$ git clone https://github.com/vuejs/vue-validator.git node_modules/vue-validator
$ cd node_modules/vue-validator
$ npm install
$ npm run build

When used in CommonJS, you must explicitly install the router via Vue.use():

var Vue = require('vue')
var VueValidator = require('vue-validator')

Vue.use(VueValidator)

You don't need to do this when using the standalone build, as it installs itself automatically.

CDN

jsdelivr

<script src="https://cdn.jsdelivr.net/vue.validator/2.0.0-beta.3/vue-validator.min.js"></script>

Usage

new Vue({
  el: '#app'
})

We can use the validator element directive and v-validate directive, as follows:

<div id="app">
  <validator name="validation1">
    <form novalidate>
      <div class="username-field">
        <label for="username">username:</label>
        <input id="username" type="text" v-validate:username="['required']">
      </div>
      <div class="comment-field">
        <label for="comment">comment:</label>
        <input id="comment" type="text" v-validate:comment="{ maxlength: 256 }">
      </div>
      <div class="errors">
        <p v-if="$validation1.username.required">Required your name.</p>
        <p v-if="$validation1.comment.maxlength">Your comment is too long.</p>
      </div>
      <input type="submit" value="send" v-if="$validation1.valid">
    </form>
  </validator>

The validation results are scoped to the validator element. In above case, the validation results keep to $validation1 scope (prefixed with $), specified by the name attribute of the validator element.

Validation result structure

Validation results can be accessed in this structure:

{
  // top-level validation properties
  valid: true,
  invalid: false,
  touched: false,
  undefined: true,
  dirty: false,
  pristine: true,
  modified: false,
  errors: [{
    field: 'field1', validator: 'required', message: 'required field1'
  }, ... {
    field: 'fieldX', validator: 'customValidator', message: 'invalid fieldX'
  }],

  // field1 validation
  field1: {
    required: false, // build-in validator, return `false` or `true`
    email: true, // custom validator
    url: 'invalid url format', // custom validator, if specify the error message in validation rule, set it
    ...
    customValidator1: false, // custom validator
    // field validation properties
    valid: false,
    invalid: true,
    touched: false,
    undefined: true,
    dirty: false,
    pristine: true,
    modified: false,
    errors: [{
      validator: 'required', message: 'required field1'
    }]
  },

  ...

  // fieldX validation
  fieldX: {
    min: false, // validator
    ...
    customValidator: true,

    // fieldX validation properties
    valid: false,
    invalid: true,
    touched: true,
    undefined: false,
    dirty: true,
    pristine: false,
    modified: true,
    errors: [{
      validator: 'customValidator', message: 'invalid fieldX'
    }]
  },
}

The various top-level properties are in the validation scope, and each field validation result in its own respective scopes.

Field validation properties

  • valid: whether field is valid; if it's valid, then return true, else return false.
  • invalid: reverse of valid.
  • touched: whether field is touched. if field was focused, return true, else return false.
  • untouched: reverse of touched.
  • modified: whether field value is modified; if field value was changed from initial value, return true, else return false.
  • dirty: whether field value was changed at least once; if so, return true, else return false.
  • pristine: reverse of dirty.
  • errors: if invalid field exist, return error message wrapped with array, else undefined.

Top level validation properties

  • valid: whether all fields is valid. if so, then return true, else return false.
  • invalid: if invalid field exist even one in validate fields, return true, else false.
  • touched: whether all fields is touched, if so, return true, else false.
  • untouched: if untouched field exist even one in validate fields, return true, else false.
  • modified: if modified field exist even one in validate fields, return true, else false.
  • dirty: if dirty field exist even one in validate fields, return true, else false.
  • pristine: whether all fields is pristine, if so, return true, else false.
  • errors: if invalid even one exist, return all field error message wrapped with array, else undefined.

Validator syntax

v-validate directive syntax the below:

v-validate[:field]="array literal | object literal | binding"

Field

In vue-validator version 2.0-alpha or earlier, validation relied on v-model. In 2.0-alpha and later, use the v-validate directive instead.

~v1.4.4:

<form novalidate>
  <input type="text" v-model="comment" v-validate="minLength: 16, maxLength: 128">
  <div>
    <span v-show="validation.comment.minLength">Your comment is too short.</span>
    <span v-show="validation.comment.maxLength">Your comment is too long.</span>
  </div>
  <input type="submit" value="send" v-if="valid">
</form>

v2.0-alpha later:

<validator name="validation">
  <form novalidate>
    <input type="text" v-validate:comment="{ minlength: 16, maxlength: 128 }">
    <div>
      <span v-show="$validation.comment.minlength">Your comment is too short.</span>
      <span v-show="$validation.comment.maxlength">Your comment is too long.</span>
    </div>
    <input type="submit" value="send" v-if="valid">
  </form>
</validator>

Caml-case property

As well as Vue.js, you can use the kebab-case for v-validate models:

<validator name="validation">
  <form novalidate>
    <input type="text" v-validate:user-name="{ minlength: 16 }">
    <div>
      <span v-if="$validation.userName.minlength">Your user name is too short.</span>
    </div>
  </form>
</validator>

Attribute

You can specify the field name to field params attribute. This is useful when you need to define the validatable form elements dynamically:

NOTE: the field part of v-validate is optional, when you use field params attribute

<div id="app">
  <validator name="validation">
    <form novalidate>
      <p class="validate-field" v-for="field in fields">
      <label :for="field.id">{{field.label}}</label>
      <input type="text" :id="field.id" :placeholder="field.placeholder" :field="field.name" v-validate="field.validate">
      </p>
      <pre>{{ $validation | json }}</pre>
    </form>
  </validator>
</div>
new Vue({
  el: '#app',
  data: {
    fields: [{
      id: 'username',
      label: 'username',
      name: 'username',
      placeholder: 'input your username',
      validate: { required: true, maxlength: 16 }
    }, {
      id: 'message',
      label: 'message',
      name: 'message',
      placeholder: 'input your message',
      validate: { required: true, minlength: 8 }
    }]
  }
})

Literal

Array

The below example uses an array literal:

<validator name="validation">
  <form novalidate>
    Zip: <input type="text" v-validate:zip="['required']"><br />
    <div>
      <span v-if="$validation.zip.required">Zip code is required.</span>
    </div>
  </form>
</validator>

Since required doesn't need to specify any additional rules, this syntax is preferred.

Object

The below example uses an object literal:

<validator name="validation">
  <form novalidate>
    ID: <input type="text" v-validate:id="{ required: true, minlength: 3, maxlength: 16 }"><br />
    <div>
      <span v-if="$validation.id.required">ID is required</span>
      <span v-if="$validation.id.minlength">Your ID is too short.</span>
      <span v-if="$validation.id.maxlength">Your ID is too long.</span>
    </div>
  </form>
</validator>

Object literals allow you to provide rule values. For required, as it doesn't need a rule value, you can specily a dummy rule instead, as shown.

Alternatively, you can specify a strict object as follows:

<validator name="validation">
  <form novalidate>
    ID: <input type="text" v-validate:id="{ minlength: { rule: 3 }, maxlength: { rule: 16 } }"><br />
    <div>
      <span v-if="$validation.id.minlength">Your ID is too short.</span>
      <span v-if="$validation.id.maxlength">Your ID is too long.</span>
    </div>
  </form>

Binding

The below example uses live binding:

new Vue({
  el: '#app',
  data: {
    rules: {
      minlength: 3,
      maxlength: 16
    }
  }
})
<div id="app">
  <validator name="validation">
    <form novalidate>
      ID: <input type="text" v-validate:id="rules"><br />
      <div>
        <span v-if="$validation.id.minlength">Your ID is too short.</span>
        <span v-if="$validation.id.maxlength">Your ID is too long.</span>
      </div>
    </form>
  </validator>
</div>

You can also use computed properties or methods to retrieve rule sets, instead of a set data property.

v-model

You can validate the field that updated with v-model:

<div id="app">
  <validator name="validation1">
    <form novalidate>
      message: <input type="text" v-model="msg" v-validate:message="{ required: true, minlength: 8 }"><br />
      <div>
        <p v-if="$validation1.message.required">Required your message.</p>
        <p v-if="$validation1.message.minlength">Too short message.</p>
      </div>
    </form>
  </validator>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    msg: ''
  }
})

setTimeout(function () {
  vm.msg = 'hello world!!'
}, 2000)

Reset validation results

You can reset the validation results with $resetValidation() Vue instance meta method that defined with validator dynamically. the below the exmpale:

<div id="app">
  <validator name="validation1">
    <form novalidate>
      <div class="username-field">
        <label for="username">username:</label>
        <input id="username" type="text" v-validate:username="['required']">
      </div>
      <div class="comment-field">
        <label for="comment">comment:</label>
        <input id="comment" type="text" v-validate:comment="{ maxlength: 256 }">
      </div>
      <div class="errors">
        <p v-if="$validation1.username.required">Required your name.</p>
        <p v-if="$validation1.comment.maxlength">Your comment is too long.</p>
      </div>
      <input type="submit" value="send" v-if="$validation1.valid">
      <button type="button" @click="onReset">Reset Validation</button>
    </form>
    <pre>{{ $validation1 | json }}</pre>
  </validator>
</div>
new Vue({
  el: '#app',
  methods: {
    onReset: function () {
      this.$resetValidation()
    }
  }
})

Checkbox

Checkbox validation supports lengths:

<div id="app">
  <validator name="validation1">
    <form novalidate>
      <h1>Survey</h1>
      <fieldset>
        <legend>Which do you like fruit ?</legend>
        <input id="apple" type="checkbox" value="apple" v-validate:fruits="{
          required: { rule: true, message: requiredErrorMsg },
          minlength: { rule: 1, message: minlengthErrorMsg },
          maxlength: { rule: 2, message: maxlengthErrorMsg }
        }">
        <label for="apple">Apple</label>
        <input id="orange" type="checkbox" value="orange" v-validate:fruits>
        <label for="orange">Orage</label>
        <input id="grape" type="checkbox" value="grape" v-validate:fruits>
        <label for="grape">Grape</label>
        <input id="banana" type="checkbox" value="banana" v-validate:fruits>
        <label for="banana">Banana</label>
        <ul class="errors">
          <li v-for="msg in $validation1.fruits.errors">
            <p>{{msg}}</p>
          </li>
        </ul>
      </fieldset>
    </form>
  </validator>
</div>
new Vue({
  el: '#app',
  computed: {
    requiredErrorMsg: function () {
      return 'Required fruit !!'
    },
    minlengthErrorMsg: function () {
      return 'Please chose at least 1 fruit !!'
    },
    maxlengthErrorMsg: function () {
      return 'Please chose at most 2 fruits !!'
    }
  }
})

Radio button

<div id="app">
  <validator name="validation1">
    <form novalidate>
      <h1>Survey</h1>
      <fieldset>
        <legend>Which do you like fruit ?</legend>
        <input id="apple" type="radio" name="fruit" value="apple" v-validate:fruits="{
          required: { rule: true, message: requiredErrorMsg }
        }">
        <label for="apple">Apple</label>
        <input id="orange" type="radio" name="fruit" value="orange" v-validate:fruits>
        <label for="orange">Orage</label>
        <input id="grape" type="radio" name="fruit" value="grape" v-validate:fruits>
        <label for="grape">Grape</label>
        <input id="banana" type="radio" name="fruit" value="banana" v-validate:fruits>
        <label for="banana">Banana</label>
        <ul class="errors">
          <li v-for="msg in $validation1.fruits.errors">
            <p>{{msg}}</p>
          </li>
        </ul>
      </fieldset>
    </form>
  </validator>
</div>
new Vue({
  el: '#app',
  computed: {
    requiredErrorMsg: function () {
      return 'Required fruit !!'
    }
  }
})

Selectbox

<div id="app">
  <validator name="validation1">
    <form novalidate>
      <select v-validate:lang="{ required: true }">
        <option value="">----- select your favorite programming language -----</option>
        <option value="javascript">JavaScript</option>
        <option value="ruby">Ruby</option>
        <option value="python">Python</option>
        <option value="perl">Perl</option>
        <option value="lua">Lua</option>
        <option value="go">Go</option>
        <option value="rust">Rust</option>
        <option value="elixir">Elixir</option>
        <option value="c">C</option>
        <option value="none">Not a nothing here</option>
      </select>
      <div class="errors">
        <p v-if="$validation1.lang.required">Required !!</p>
      </div>
    </form>
  </validator>
</div>
new Vue({ el: '#app' })

Grouping

The vue binding syntax can group inputs together:

<validator name="validation1" :groups="['user', 'password']">
  username: <input type="text" group="user" v-validate:username="['required']"><br />
  password: <input type="password" group="password" v-validate:password1="{ minlength: 8, required: true }"/><br />
  password (confirm): <input type="password" group="password" v-validate:password2="{ minlength: 8, required: true }"/>
  <div class="user">
    <span v-if="$validation1.user.invalid">Invalid yourname !!</span>
  </div>
  <div class="password">
    <span v-if="$validation1.password.invalid">Invalid password input !!</span>
  </div>
</validator>

Error messages

Error messages can be stored directly in the validation rules, and can be use error message on v-show or v-if:

<validator name="validation1">
  <div class="username">
    <label for="username">username:</label>
    <input id="username" type="text" v-validate:username="{
      required: { rule: true, message: 'required you name !!' }
    }">
    <span v-if="$validation1.username.required">{{ $validation1.username.required }}</span>
  </div>
  <div class="password">
    <label for="password">password:</label>
    <input id="password" type="password" v-validate:password="{
      required: { rule: true, message: 'required you password !!' },
      minlength: { rule: 8, message: 'your password short too !!' }
    }"/>
    <span v-if="$validation1.password.required">{{ $validation1.password.required }}</span>
    <span v-if="$validation1.password.minlength">{{ $validation1.password.minlength }}</span>
  </div>
</validator>

Also, Error message can be used with v-for:

<validator name="validation1">
  <div class="username">
    <label for="username">username:</label>
    <input id="username" type="text" v-validate:username="{
      required: { rule: true, message: 'required you name !!' }
    }">
  </div>
  <div class="password">
    <label for="password">password:</label>
    <input id="password" type="password" v-validate:password="{
      required: { rule: true, message: 'required you password !!' },
      minlength: { rule: 8, message: 'your password short too !!' }
    }"/>
  </div>
  <div class="errors">
    <ul>
      <li v-for="error in $validation1.errors">
        <p>{{error.field}}: {{error.mesage}}</p>
      </li>
    </ul>
  </div>
</validator>

Data property or computed properties can help reduce clutter, rather than using inline rule sets.

Error message enumeration component

In the above example, we used v-for directive to enumerate errors of validator. But, we can't be bothered to do it. So, vue-validator provide the convenient validator-errors component to enumerate error info. the following the example:

<validator name="validation1">
  <div class="username">
    <label for="username">username:</label>
    <input id="username" type="text" v-validate:username="{
      required: { rule: true, message: 'required you name !!' }
    }">
  </div>
  <div class="password">
    <label for="password">password:</label>
    <input id="password" type="password" v-validate:password="{
      required: { rule: true, message: 'required you password !!' },
      minlength: { rule: 8, message: 'your password short too !!' }
    }"/>
  </div>
  <div class="errors">
    <validator-errors :validation="$validation1"></validator-errors>
  </div>
</validator>

In the above example, it's rendered the following:

<div class="username">
  <label for="username">username:</label>
  <input id="username" type="text">
</div>
<div class="password">
  <label for="password">password:</label>
  <input id="password" type="password">
</div>
<div class="errors">
  <div>
    <p>password: your password short too !!</p>
  </div>
  <div>
    <p>password: required you password !!</p>
  </div>
  <div>
    <p>username: required you name !!</p>
  </div>
</div>

Custom error message templating

If you don't like the default error message format of validator-errors, you can specify the custom error message template with your component or your partial template.

Component template

the below component example:

<div id="app">
  <validator name="validation1">
    <div class="username">
      <label for="username">username:</label>
      <input id="username" type="text" v-validate:username="{
        required: { rule: true, message: 'required you name !!' }
      }">
    </div>
    <div class="password">
      <label for="password">password:</label>
      <input id="password" type="password" v-validate:password="{
        required: { rule: true, message: 'required you password !!' },
        minlength: { rule: 8, message: 'your password short too !!' }
      }"/>
    </div>
    <div class="errors">
      <validator-errors :component="'custom-error'" :validation="$validation1">
      </validator-errors>
    </div>
  </validator>
</div>
// register the your component with Vue.component
Vue.component('custom-error', {
  props: ['field', 'validator', 'message'],
  template: '<p class="error-{{field}}-{{validator}}">{{message}}</p>'
})

new Vue({ el: '#app' })

Partial template

the below partial template example:

<div id="app">
  <validator name="validation1">
    <div class="username">
      <label for="username">username:</label>
      <input id="username" type="text" v-validate:username="{
        required: { rule: true, message: 'required you name !!' }
      }">
    </div>
    <div class="password">
      <label for="password">password:</label>
      <input id="password" type="password" v-validate:password="{
        required: { rule: true, message: 'required you password !!' },
        minlength: { rule: 8, message: 'your password short too !!' }
      }"/>
    </div>
    <div class="errors">
      <validator-errors partial="myErrorTemplate" :validation="$validation1">
      </validator-errors>
    </div>
  </validator>
</div>
// register custom error template
Vue.partial('myErrorTemplate', '<p>{{field}}: {{validator}}: {{message}}</p>')
new Vue({ el: '#app' })

Error messages focusing

Sometimes, you need to output the part of error messages. You can focus the part of validation results when you use the group or field attributes.

  • group: error messages of the group in validation results (e.g. $validation.group1.errors)
  • field: error messages of the field in validation results (e.g. $validation.field1.errors)

the below group attribute example:

<div id="app">
  <validator :groups="['profile', 'password']" name="validation1">
    <div class="username">
      <label for="username">username:</label>
      <input id="username" type="text" group="profile" v-validate:username="{
        required: { rule: true, message: 'required you name !!' }
      }">
    </div>
    <div class="url">
      <label for="url">url:</label>
      <input id="url" type="text" group="profile" v-validate:url="{
        required: { rule: true, message: 'required you name !!' },
        url: { rule: true, message: 'invalid url format' }
      }">
    </div>
     <div class="old">
     <label for="old">old password:</label>
      <input id="old" type="password" group="password" v-validate:old="{
        required: { rule: true, message: 'required you old password !!' },
        minlength: { rule: 8, message: 'your old password short too !!' }
      }"/>
    </div>
    <div class="new">
      <label for="new">new password:</label>
      <input id="new" type="password" group="password" v-validate:new="{
        required: { rule: true, message: 'required you new password !!' },
        minlength: { rule: 8, message: 'your new password short too !!' }
      }"/>
    </div>
    <div class="confirm">
      <label for="confirm">confirm password:</label>
      <input id="confirm" type="password" group="password" v-validate:confirm="{
        required: { rule: true, message: 'required you confirm password !!' },
        minlength: { rule: 8, message: 'your confirm password short too !!' }
      }"/>
    </div>
    <div class="errors">
      <validator-errors group="profile" :validation="$validation1">
      </validator-errors>
    </div>
  </validator>
</div>
Vue.validator('url', function (val) {
  return /^(http\:\/\/|https\:\/\/)(.{4,})$/.test(val)
})
new Vue({ el: '#app' })

Manually error message settings

Sometimes, you need to manually set the validation error message such as server-side validation error. At that time, you can apply some error messages to validation results with using $setValidationErrors meta method.

vm.$setValidationErrors(erros)

  • Arguments:

    • Array<Object> errors
      • {String} field
      • {String} message
      • {String} validator [optional]
  • Usage:

    Set the errors to validation result errors. This is useful when you want to set manually some errors of server-side validation.

  • Example:

      <div id="app">
        <validator name="validation">
          <div class="username">
            <label for="username">username:</label>
            <input id="username" type="text" v-model="username" v-validate:username="{
              required: { rule: true, message: 'required you name !!' }
            }">
          </div>
          <div class="old">
            <label for="old">old password:</label>
            <input id="old" type="password" v-model="passowrd.old" v-validate:old="{
              required: { rule: true, message: 'required you old password !!' }
            }"/>
          </div>
          <div class="new">
            <label for="new">new password:</label>
            <input id="new" type="password" v-model="password.new" v-validate:new="{
              required: { rule: true, message: 'required you new password !!' },
              minlength: { rule: 8, message: 'your new password short too !!' }
            }"/>
          </div>
          <div class="confirm">
            <label for="confirm">confirm password:</label>
            <input id="confirm" type="password" v-validate:confirm="{
              required: { rule: true, message: 'required you confirm password !!' },
              confirm: { rule: passowd.new, message: 'your confirm password incorrect !!' }
            }"/>
          </div>
          <div class="errors">
            <validator-errors :validation="$validation"></validator-errors>
          </div>
          <button type="button" v-if="$validation.valid" @click.prevent="onSubmit">update</button>
        </validator>
      </div>
      new Vue({
        el: '#app',
        data: {
          id: 1,
          username: '',
          password: {
            old: '',
            new: ''
          }
        },
        validators: {
          confirm: function (val, target) {
            return val === target
          }
        },
        methods: {
          onSubmit: function () {
            var self = this
            var resource = this.$resource('/user/:id')
            resource.save({ id: this.id }, {
              username: this.username,
              passowrd: this.new
            }, function (data, stat, req) {
              // something handle success ...
              // ...
            }).error(function (data, stat, req) {
              // handle server error
              self.$setValidationErrors([
                { field: data.field, message: data.message }
              ])
            })
          }
        }
      })
  • Argument: field

    To detect as validation field error, you need to pass in field argument.

  • Argument: message

    To output as validation error messsage, you need to pass in message argument.

  • Argument: validator

    In order to detect where the validator error occurred, you pass in validator argument.

Event

Using regular vue event bindings, you can be bound the event that has occured with validator.

Field validation event

You can be handled the event to each field properties, like v-validate directive specified to input tag:

  • valid: occure when validation result of each field became valid
  • invalid: occure when validation result of each field became invalid
  • touched: occure when each field detected blur at least once
  • dirty: occure when the value of each field changed from initial value at least once
  • modified: occure when the value of each field changed from initial value
<div id="app">
  <validator name="validation1">
    <div class="comment-field">
      <label for="comment">comment:</label>
      <input type="text" 
             @valid="onValid" 
             @invalid="onInvalid" 
             @touched="onTouched" 
             @dirty="onDirty" 
             @modified="onModified"
             v-validate:comment="['required']"/>
    </div>
    <div>
      <p>{{occuredValid}}</p>
      <p>{{occuredInvalid}}</p>
      <p>{{occuredTouched}}</p>
      <p>{{occuredDirty}}</p>
      <p>{{occuredModified}}</p>
    </div>
  </validator>
</div>
new Vue({
  el: '#app',
  data: {
    occuredValid: '',
    occuredInvalid: '',
    occuredTouched: '',
    occuredDirty: '',
    occuredModified: ''
  },
  methods: {
    onValid: function () {
      this.occuredValid = 'occured valid event'
      this.occuredInvalid = ''
    },
    onInvalid: function () {
      this.occuredInvalid = 'occured invalid event'
      this.occuredValid = ''
    },
    onTouched: function () {
      this.occuredTouched = 'occured touched event'
    },
    onDirty: function () {
      this.occuredDirty = 'occured dirty event'
    },
    onModified: function (e) {
      this.occuredModified = 'occured modified event: ' + e.modified
    }
  }
})

Top level validation event

You can be handled the event that has occured in the various top-level validation properties:

  • valid: occure when validation result of top-level became valid
  • invalid: occure when validation result of top-level became invalid
  • touched: occure when top-level detected blur at least once
  • dirty: occure when the value of top-level changed from initial value at least once
  • modified: occure when the value of top-level changed from initial value
<div id="app">
  <validator name="validation1"
             @valid="onValid"
             @invalid="onInvalid"
             @touched="onTouched"
             @dirty="onDirty"
             @modified="onModified">
    <div class="comment-field">
      <label for="username">username:</label>
      <input type="text" 
             v-validate:username="['required']"/>
    </div>
    <div class="password-field">
      <label for="password">password:</label>
      <input type="password" 
             v-validate:password="{ required: true, minlength: 8 }"/>
    </div>
    <div>
      <p>{{occuredValid}}</p>
      <p>{{occuredInvalid}}</p>
      <p>{{occuredTouched}}</p>
      <p>{{occuredDirty}}</p>
      <p>{{occuredModified}}</p>
    </div>
  </validator>
</div>
new Vue({
  el: '#app',
  data: {
    occuredValid: '',
    occuredInvalid: '',
    occuredTouched: '',
    occuredDirty: '',
    occuredModified: ''
  },
  methods: {
    onValid: function () {
      this.occuredValid = 'occured valid event'
      this.occuredInvalid = ''
    },
    onInvalid: function () {
      this.occuredInvalid = 'occured invalid event'
      this.occuredValid = ''
    },
    onTouched: function () {
      this.occuredTouched = 'occured touched event'
    },
    onDirty: function () {
      this.occuredDirty = 'occured dirty event'
    },
    onModified: function (modified) {
      this.occuredModified = 'occured modified event: ' + modified
    }
  }
})

Lazy initialization

The lazy attribute on the validator element will delay initialization of the validator until $activateValidator() is called. This is useful for data that must first be loaded in asynchronously, preventing the validator from reporting invalid data until ready.

The following example waits for the comment contents to be loaded before evaluating; without lazy, the component would show errors until the data loads in.

<!-- comment component -->
<div>
  <h1>Preview</h1>
  <p>{{comment}}</p>
  <validator lazy name="validation1">
    <input type="text" :value="comment" v-validate:comment="{ required: true, maxlength: 256 }"/>
    <span v-if="$validation1.comment.required">Required your comment</span>
    <span v-if="$validation1.comment.maxlength">Too long comment !!</span>
    <button type="button" value="save" @click="onSave" v-if="valid">
  </validator>
</div>
Vue.component('comment', {
  props: {
    id: Number,
  },
  data: function () {
    return { comment: '' }
  },
  activate: function (done) {
    var resource = this.$resource('/comments/:id');
    resource.get({ id: this.id }, function (comment, stat, req) {
      this.commont =  comment.body

      // activate validator
      this.$activateValidator()
      done()

    }.bind(this)).error(function (data, stat, req) {
      // handle error ...
      done()
    })
  },
  methods: {
    onSave: function () {
      var resource = this.$resource('/comments/:id');
      resource.save({ id: this.id }, { body: this.comment }, function (data, stat, req) {
        // handle success
      }).error(function (data, sta, req) {
        // handle error
      })
    }
  }
})

Custom validator

Global registration

You can register your custom validator with using Vue.validator method.

NOTE: Vue.validator asset is extended from Vue.js' asset managment system.

Detail of the Vue.validator method is following:

Vue.validator(id, [definition])

  • Arguments:
    • {String} id
    • {Function | Object} [definition]
  • Return:
    • validator definition function or object

In validator definition function or check of validator definition object, you need to return true if valid, else return false.

the below the email custom validator exmpale:

// Register custom validator function. 
// - first argument: field value
// - second argument: rule value (optional). this argument is being passed from specified validator rule with v-validate
Vue.validator('email', function (val/*,rule*/) {
  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(val)
})

new Vue({
  el: '#app'
  data: {
    email: ''
  }
})
<div id="app">
  <validator name="validation1">
    address: <input type="text" v-validate:address="['email']"><br />
    <div>
      <p v-show="$validation1.address.email">Invalid your mail address format.</p>
    </div>
  <validator>
</div>

Local registration

You can register your custom validator to component with using validators option.

Cursom validators are registered to Vue constructor validators option using a callback function; return true upon passing.

the below the numeric or url custom validator exmpale:

new Vue({
  el: '#app',
  validators: { // `numeric` and `url` custom validator is local registration
    numeric: function (val/*,rule*/) {
      return /^[-+]?[0-9]+$/.test(val)
    },
    url: function (val) {
      return /^(http\:\/\/|https\:\/\/)(.{4,})$/.test(val)
    }
  },
  data: {
    email: ''
  }
})
<div id="app">
  <validator name="validation1">
    username: <input type="text" v-validate:username="['required']"><br />
    email: <input type="text" v-validate:address="['email']"><br />
    age: <input type="text" v-validate:age="['numeric']"><br />
    site: <input type="text" v-validate:site="['url']"><br />
    <div class="errors">
      <p v-if="$validation1.username.required">required username</p>
      <p v-if="$validation1.address.email">invalid email address</p>
      <p v-if="$validation1.age.numeric">invalid age value</p>
      <p v-if="$validation1.site.url">invalid site uril format</p>
    </div>
  <validator>
</div>

Error message

Custom validators may have default error messages attached:

// `email` custom validator global registration
Vue.validator('email', {
  message: 'invalid email address', // error message with plain string
  check: function (val) { // define validator
    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(val)
  }
})

// build-in `required` validator customization
Vue.validator('required', {
  message: function (field) { // error message with function
    return 'required "' + field + '" field'
  },
  check: Vue.validator('required') // re-use validator logic
})

new Vue({
  el: '#app',
  validators: {
    numeric: { // `numeric` custom validator local registration
      message: 'invalid numeric value',
      check: function (val) {
        return /^[-+]?[0-9]+$/.test(val)
      }
    },
    url: { // `url` custom validator local registration
      message: function (field) {
        return 'invalid "' + field + '" url format field'
      },
      check: function (val) {
        return /^(http\:\/\/|https\:\/\/)(.{4,})$/.test(val)
      }
    }
  },
  data: {
    email: ''
  }
})
<div id="app">
  <validator name="validation1">
    username: <input type="text" v-validate:username="['required']"><br />
    email: <input type="text" v-validate:address="['email']"><br />
    age: <input type="text" v-validate:age="['numeric']"><br />
    site: <input type="text" v-validate:site="['url']"><br />
    <div class="errors">
      <validator-errors :validation="$validation1"></validator-errors>
    </div>
  <validator>
</div>

Validation timing customization

vue-validator validate automatically with validator element directive and v-validate directive. However, sometimes, we are disabling automatically validation, and there are times we want to validate manually.

initial

When vue-validator completed initial compilation, each v-validate directive automatically validate target element. if you don't hope that behavior, you can specify with initial attribute, or v-validate syntax:

<div id="app">
  <validator name="validation1">
    <form novalidate>
      <div class="username-field">
        <label for="username">username:</label>
        <!-- 'inital' attribute is applied the all validators of target element (e.g. required, exist) -->
        <input id="username" type="text" inital="off" v-validate:username="['required', 'exist']">
      </div>
      <div class="password-field">
        <label for="password">password:</label>
        <!-- 'initial' optional is applied with `v-validate` validator (e.g. required only) -->
        <input id="password" type="password" v-validate:passowrd="{ required: { rule: true, initial: 'off' }, minlength: 8 }">
      </div>
      <input type="submit" value="send" v-if="$validation1.valid">
    </form>
  </validator>
</div>

This is useful, when you need to suppress the validation (like the server-side validation) with async validation feature (explain later).

detect-blur and detect-change

vue-validator validate automatically when detect DOM event (input, blur, change) in formalable elements (input, checkbox, select, etc). In the case, use the detect-change, detect-blur attributes:

<div id="app">
  <validator name="validation">
    <form novalidate @submit="onSubmit">
      <h1>user registration</h1>
      <div class="username">
        <label for="username">username:</label>
        <input id="username" type="text" 
          detect-change="off" detect-blur="off" v-validate:username="{
          required: { rule: true, message: 'required you name !!' }
        }" />
      </div>
      <div class="password">
        <label for="password">password:</label>
        <input id="password" type="password" v-model="password" 
          detect-change="off" detect-blur="off" v-validate:password="{
          required: { rule: true, message: 'required you new password !!' },
          minlength: { rule: 8, message: 'your new password short too !!' }
        }" />
      </div>
      <div class="confirm">
        <label for="confirm">confirm password:</label>
        <input id="confirm" type="password" 
          detect-change="off" detect-blur="off" v-validate:confirm="{
          required: { rule: true, message: 'required you confirm password !!' },
          confirm: { rule: password, message: 'your confirm password incorrect !!' }
        }" />
      </div>
      <div class="errors" v-if="$validation.touched">
        <validator-errors :validation="$validation"></validator-errors>
      </div>
      <input type="submit" value="register" />
    </form>
  </validator>
</div>
new Vue({
  el: '#app',
  data: {
    password: ''
  },
  validators: {
    confirm: function (val, target) {
      return val === target
    }
  },
  methods: {
    onSubmit: function (e) {
      // validate manually
      this.$validate(true)

      if (this.$validation.invalid) {
        e.preventDefault()
      }
    }
  }
})

Async validation

You can use the async validation. This is useful, when you need to use the server-side validation. the below the example:

<template>
  <validator name="validation">
    <form novalidate>
      <h1>user registration</h1>
      <div class="username">
        <label for="username">username:</label>
        <input id="username" type="text" 
          detect-change="off" v-validate:username="{
          required: { rule: true, message: 'required your name !!' },
          exist: { rule: true, initial: 'off' }
        }" />
        <span v-if="checking">checking ...</span>
      </div>
      <div class="errors">
        <validator-errors :validation="$validation"></validator-errors>
      </div>
      <input type="submit" value="register" :disabled="!$validation.valid" />
    </form>
  </validator>
</template>
function copyOwnFrom (target, source) {
  Object.getOwnPropertyNames(source).forEach(function (propName) {
    Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName))
  })
  return target
}

function ValidationError () {
  copyOwnFrom(this, Error.apply(null, arguments))
}
ValidationError.prototype = Object.create(Error.prototype)
ValidationError.prototype.constructor = ValidationError

// exmpale with ES2015
export default {
  validators: {
    data () {
      return { checking: false }
    },
    exist (val) {
      this.vm.checking = true // spinner on
      return fetch('/validations/exist', {
        method: 'post',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          username: val
        })
      }).then((res) => {
        this.vm.checking = false // spinner off
        return res.json()
      }).then((json) => {
        return Object.keys(json).length > 0 
          ? Promise.reject(new ValidationError(json.message))
          : Promise.resolve()
      }).catch((error) => {
        if (error instanceof ValidationError) {
          return Promise.reject(error.message)
        } else {
          return Promise.reject('unexpected error')
        }
      })
    }
  }
}

Async validation interfaces

In async validation, You can use the two type interfaces:

1. function

You need to implement custom validator that return function have function (resolve, reject) like promise (future). The following, those argument of the function, you need to use according to validation result.

  • validation result
    • successful: resolve
    • failed: reject

2. promise

As mentioned above, You need to implement custom validation that return a promise. you need to resolve or reject according to validation result.

Using error message

As mentioned above, when server-side validation error occured, you can use the server-side error message.

Validator function context

Validator function context is bind with Validation object. Validation object expose the some properties. These properties is useful when you need to implement specially validation.

vm property

Expose the vue instance of current validation.

the following ES2015 example:

new Vue({
  data () { return { checking: false } },
  validators: {
    exist (val) {
      this.vm.checking = true // spinner on
      return fetch('/validations/exist', {
        // ...
      }).then((res) => { // done
        this.vm.checking = false // spinner off
        return res.json()
      }).then((json) => {
        return Promise.resolve()
      }).catch((error) => {
        return Promise.reject(error.message)
      })
    }
  }
})

el property

Expose the target DOM element of current validation. In the case, use International Telephone Input jQuery plugin example:

new Vue({
  validators: {
    phone: function (val) {
      return $(this.el).intlTelInput('isValidNumber')
    }
  }
})

TODO

Contributing

  • Fork it !
  • Create your top branch from dev: git branch my-new-topic origin/dev
  • Commit your changes: git commit -am 'Add some topic'
  • Push to the branch: git push origin my-new-topic
  • Submit a pull request to dev branch of vuejs/vue-validator repository !

Testing

$ npm test

License

MIT

About

Validator component for Vue.js

License:MIT License


Languages

Language:JavaScript 88.6%Language:HTML 11.0%Language:Vue 0.5%