Vuelidate 中文网(0.7.4)

Vue.js 2.0简单的,轻量级基于模型的验证

  • 上下文验证器
  • 易于与自定义验证器一起使用(例如Moment.js)
  • 支持功能组合
  • 验证不同的数据源:Vuex获取器,计算值等。
  • 高测试覆盖率

入门


包装内容

Vue.js的简单,轻量级基于模型的验证

您可以阅读介绍文章,以了解有关此解决方案与其他验证库的区别的更多信息。

安装

软件包可通过npm安装

npm install vuelidate --save

基本用法

您可以导入库并将其use作为Vue插件导入,以在包含验证配置的所有组件上全局启用该功能。

import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)

另外,也可以将混合直接导入到将使用混合的组件中。

import { validationMixin } from 'vuelidate'
var Component = Vue.extend({
  mixins: [validationMixin],
  validations: { ... }
})

如果您更喜欢使用require,则可以使用它代替import 语句。这特别适用于解构语法。

const { validationMixin, default: Vuelidate } = require('vuelidate')
const { required, minLength } = require('vuelidate/lib/validators')

软件包中还提供了支持浏览器的捆绑软件。

<script src="vuelidate/dist/vuelidate.min.js"></script>
// global
Vue.use(window.vuelidate.default)
// local mixin
var validationMixin = window.vuelidate.validationMixin

查看使用此设置的JSFiddle示例

例子


基本形式

对于每个要验证的值,您必须在validations选项内部创建一个键。您可以通过在输入框上使用适当的事件来指定输入何时变脏。

请注意,在此示例中,红色边框,红色文本和错误消息的可见性form-group--error取决于周围是否存在类div。除非使用类似方法或使用不带v-model的验证(下一部分),否则与验证错误有关的任何标记都会在初始加载时显示。

import { required, minLength, between } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      name: '',
      age: 0
    }
  },
  validations: {
    name: {
      required,
      minLength: minLength(4)
    },
    age: {
      between: between(20, 30)
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="$v.name.$model")
  .error(v-if="!$v.name.required") Field is required
  .error(v-if="!$v.name.minLength")
    | Name must have at least {{$v.name.$params.minLength.min}} letters.
  tree-view(:data="$v.name", :options="{rootObjectKey: '$v.name', maxDepth: 2}")
  .form-group(:class="{ 'form-group--error': $v.age.$error }")
    label.form__label Age
    input.form__input(v-model.trim.lazy="$v.age.$model")
  .error(v-if="!$v.age.between")
    | Must be between {{$v.age.$params.between.min}} and {{$v.age.$params.between.max}}
  span(tabindex="0") Blur to see changes
  tree-view(:data="$v.age", :options="{rootObjectKey: '$v.age', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="$v.name.$model"/>
  </div>
  <div class="error" v-if="!$v.name.required">Field is required</div>
  <div class="error" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
  <tree-view :data="$v.name" :options="{rootObjectKey: '$v.name', maxDepth: 2}"></tree-view>
  <div class="form-group" :class="{ 'form-group--error': $v.age.$error }">
    <label class="form__label">Age</label>
    <input class="form__input" v-model.trim.lazy="$v.age.$model"/>
  </div>
  <div class="error" v-if="!$v.age.between">Must be between {{$v.age.$params.between.min}} and {{$v.age.$params.between.max}}</div><span tabindex="0">Blur to see changes</span>
  <tree-view :data="$v.age" :options="{rootObjectKey: '$v.age', maxDepth: 2}"></tree-view>
</div>

没有v模型

如果您不想直接修改模型,仍然可以使用单独:input@event绑定。如果您正在使用来自外部源(例如Vuex商店或道具)的数据,这将特别有用。在这种情况下,您必须在适当的时候手动设置$dirtyby调用$touch()方法。


import { required, minLength, between } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      name: '',
      age: 0
    }
  },
  validations: {
    name: {
      required,
      minLength: minLength(4)
    },
    age: {
      between: between(20, 30)
    }
  },
  methods: {
    setName(value) {
      this.name = value
      this.$v.name.$touch()
    },
    setAge(value) {
      this.age = value
      this.$v.age.$touch()
    }
  }
}

div
  .form-group(:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="name", @input="setName($event.target.value)")
  .error(v-if="!$v.name.required") Field is required
  .error(v-if="!$v.name.minLength")
    | Name must have at least {{$v.name.$params.minLength.min}} letters.
  .form-group(:class="{ 'form-group--error': $v.age.$error }")
    label.form__label Age
    input.form__input(:value="age" @change="setAge($event.target.value)")
  .error(v-if="!$v.age.between")
    | Must be between {{$v.age.$params.between.min}} and {{$v.age.$params.between.max}}
  span(tabindex="0") Blur to see changes

<div>
  <div class="form-group" :class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="name" @input="setName($event.target.value)"/>
  </div>
  <div class="error" v-if="!$v.name.required">Field is required</div>
  <div class="error" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.age.$error }">
    <label class="form__label">Age</label>
    <input class="form__input" :value="age" @change="setAge($event.target.value)"/>
  </div>
  <div class="error" v-if="!$v.age.between">Must be between {{$v.age.$params.between.min}} and {{$v.age.$params.between.max}}</div><span tabindex="0">Blur to see changes</span>
</div>

表格提交

经验证的表单的常见操作是在提交之前检查其有效性。您可以通过$invalid在发送任何请求之前检查状态来轻松完成此操作。

import { required, minLength } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      name: '',
      age: 0,
      submitStatus: null
    }
  },
  validations: {
    name: {
      required,
      minLength: minLength(4)
    }
  },
  methods: {
    submit() {
      console.log('submit!')
      this.$v.$touch()
      if (this.$v.$invalid) {
        this.submitStatus = 'ERROR'
      } else {
        // do your submit logic here
        this.submitStatus = 'PENDING'
        setTimeout(() => {
          this.submitStatus = 'OK'
        }, 500)
      }
    }
  }
}
form(@submit.prevent="submit")
  .form-group(:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="$v.name.$model")
  .error(v-if="!$v.name.required") Name is required
  .error(v-if="!$v.name.minLength")
    | Name must have at least {{$v.name.$params.minLength.min}} letters.
  button.button(type="submit" :disabled="submitStatus === 'PENDING'") Submit!
  p.typo__p(v-if="submitStatus === 'OK'") Thanks for your submission!
  p.typo__p(v-if="submitStatus === 'ERROR'") Please fill the form correctly.
  p.typo__p(v-if="submitStatus === 'PENDING'") Sending...
<form @submit.prevent="submit">
  <div class="form-group" :class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="$v.name.$model"/>
  </div>
  <div class="error" v-if="!$v.name.required">Name is required</div>
  <div class="error" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
  <button class="button" type="submit" :disabled="submitStatus === 'PENDING'">Submit!</button>
  <p class="typo__p" v-if="submitStatus === 'OK'">Thanks for your submission!</p>
  <p class="typo__p" v-if="submitStatus === 'ERROR'">Please fill the form correctly.</p>
  <p class="typo__p" v-if="submitStatus === 'PENDING'">Sending...</p>
</form>

上下文验证器

您可以通过上下文验证器链接相关字段。sameAs内置验证器的一个示例。

import { required, sameAs, minLength } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      password: '',
      repeatPassword: ''
    }
  },
  validations: {
    password: {
      required,
      minLength: minLength(6)
    },
    repeatPassword: {
      sameAsPassword: sameAs('password')
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.password.$error }")
    label.form__label Password
    input.form__input(v-model.trim="$v.password.$model")
  .error(v-if="!$v.password.required") Password is required.
  .error(v-if="!$v.password.minLength")
    | Password must have at least {{ $v.password.$params.minLength.min }} letters.
  .form-group(:class="{ 'form-group--error': $v.repeatPassword.$error }")
    label.form__label Repeat password
    input.form__input(v-model.trim="$v.repeatPassword.$model")
  .error(v-if="!$v.repeatPassword.sameAsPassword") Passwords must be identical.
  tree-view(:data="$v", :options="{rootObjectKey: '$v', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.password.$error }">
    <label class="form__label">Password</label>
    <input class="form__input" v-model.trim="$v.password.$model"/>
  </div>
  <div class="error" v-if="!$v.password.required">Password is required.</div>
  <div class="error" v-if="!$v.password.minLength">Password must have at least {{ $v.password.$params.minLength.min }} letters.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.repeatPassword.$error }">
    <label class="form__label">Repeat password</label>
    <input class="form__input" v-model.trim="$v.repeatPassword.$model"/>
  </div>
  <div class="error" v-if="!$v.repeatPassword.sameAsPassword">Passwords must be identical.</div>
  <tree-view :data="$v" :options="{rootObjectKey: '$v', maxDepth: 2}"></tree-view>
</div>

数据嵌套

您可以嵌套验证器以根据需要深度匹配数据。父验证器是$invalid其任何子验证器报告$invalid状态的时间。这对于整体表单验证可能非常有用。

import { required } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      form: {
        nestedA: '',
        nestedB: ''
      }
    }
  },
  validations: {
    form: {
      nestedA: {
        required
      },
      nestedB: {
        required
      }
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.form.nestedA.$error }")
    label.form__label Nested A
    input.form__input(v-model.trim="$v.form.nestedA.$model")
  .error(v-if="!$v.form.nestedA.required") Field is required.
  .form-group(:class="{ 'form-group--error': $v.form.nestedB.$error }")
    label.form__label Nested B
    input.form__input(v-model.trim="$v.form.nestedB.$model")
  .error(v-if="!$v.form.nestedB.required") Field is required.
  .form-group(:class="{ 'form-group--error': $v.form.$error }")
  .error(v-if="$v.form.$error") Form is invalid.
  tree-view(:data="$v.form", :options="{rootObjectKey: '$v.form', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.form.nestedA.$error }">
    <label class="form__label">Nested A</label>
    <input class="form__input" v-model.trim="$v.form.nestedA.$model"/>
  </div>
  <div class="error" v-if="!$v.form.nestedA.required">Field is required.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.form.nestedB.$error }">
    <label class="form__label">Nested B</label>
    <input class="form__input" v-model.trim="$v.form.nestedB.$model"/>
  </div>
  <div class="error" v-if="!$v.form.nestedB.required">Field is required.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.form.$error }"></div>
  <div class="error" v-if="$v.form.$error">Form is invalid.</div>
  <tree-view :data="$v.form" :options="{rootObjectKey: '$v.form', maxDepth: 2}"></tree-view>
</div>

$error vs $anyError

有两种常见的方式来考虑是否应显示错误。重要的是要了解哪个更适合您的用例。您可以使用$error$anyError验证属性,或通过扩展使用低级变体:$dirty$anyDirty。请注意,本文档$error在示例中主要使用了变体,但您可以自行选择。

import { required, minLength } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      fieldA: '',
      fieldB: ''
    }
  },
  validations: {
    fieldA: {
      required,
      minLength: minLength(3)
    },
    fieldB: {
      required,
      minLength: minLength(3)
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.fieldA.$error }")
    label.form__label Field A
    input.form__input(v-model.trim="$v.fieldA.$model")
  .error(v-if="!$v.fieldA.required") Field A is required.
  .error(v-if="!$v.fieldA.minLength")
    | Field A must have at least {{$v.fieldA.$params.minLength.min}} letters.
  .form-group(:class="{ 'form-group--error': $v.fieldB.$error }")
    label.form__label Field B
    input.form__input(v-model.trim="$v.fieldB.$model")
  .error(v-if="!$v.fieldB.required") Field B is required.
  .error(v-if="!$v.fieldB.minLength")
    | Field B must have at least {{$v.fieldB.$params.minLength.min}} letters.
  .form-group
    button.button(@click="$v.$reset") $reset
  .form-group
    label.form__label Validation status:
    ul.list__ul
      li(v-if="$v.fieldA.$invalid") Field A is <kbd>$invalid</kbd>.
      li(v-if="$v.fieldA.$error") Field A has <kbd>$error</kbd> and <kbd>$anyError</kbd>.
      li(v-if="$v.fieldB.$invalid") Field B is <kbd>$invalid</kbd>.
      li(v-if="$v.fieldB.$error") Field B has <kbd>$error</kbd> and <kbd>$anyError</kbd>.
      li(v-if="$v.$invalid") Form is <kbd>$invalid</kbd>.
      li(v-else) All fine.
      li(v-if="$v.$error"): strong Form has <kbd>$error</kbd>.
      li(v-if="$v.$anyError"): strong Form has <kbd>$anyError</kbd>.
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.fieldA.$error }">
    <label class="form__label">Field A</label>
    <input class="form__input" v-model.trim="$v.fieldA.$model"/>
  </div>
  <div class="error" v-if="!$v.fieldA.required">Field A is required.</div>
  <div class="error" v-if="!$v.fieldA.minLength">Field A must have at least {{$v.fieldA.$params.minLength.min}} letters.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.fieldB.$error }">
    <label class="form__label">Field B</label>
    <input class="form__input" v-model.trim="$v.fieldB.$model"/>
  </div>
  <div class="error" v-if="!$v.fieldB.required">Field B is required.</div>
  <div class="error" v-if="!$v.fieldB.minLength">Field B must have at least {{$v.fieldB.$params.minLength.min}} letters.</div>
  <div class="form-group">
    <button class="button" @click="$v.$reset">$reset</button>
  </div>
  <div class="form-group">
    <label class="form__label">Validation status:</label>
    <ul class="list__ul">
      <li v-if="$v.fieldA.$invalid">Field A is <kbd>$invalid</kbd>.</li>
      <li v-if="$v.fieldA.$error">Field A has <kbd>$error</kbd> and <kbd>$anyError</kbd>.</li>
      <li v-if="$v.fieldB.$invalid">Field B is <kbd>$invalid</kbd>.</li>
      <li v-if="$v.fieldB.$error">Field B has <kbd>$error</kbd> and <kbd>$anyError</kbd>.</li>
      <li v-if="$v.$invalid">Form is <kbd>$invalid</kbd>.</li>
      <li v-else="v-else">All fine.</li>
      <li v-if="$v.$error"><strong>Form has <kbd>$error</kbd>.</strong></li>
      <li v-if="$v.$anyError"><strong>Form has <kbd>$anyError</kbd>.</strong></li>
    </ul>
  </div>
</div>

验证组

如果要创建一个将许多其他不相关字段分组在一起的验证器,则可以创建一个验证组。

import { required } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      flatA: '',
      flatB: '',
      forGroup: {
        nested: ''
      }
    }
  },
  validations: {
    flatA: { required },
    flatB: { required },
    forGroup: {
      nested: { required }
    },
    validationGroup: ['flatA', 'flatB', 'forGroup.nested']
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.flatA.$error }")
    label.form__label Flat A
    input.form__input(v-model.trim="$v.flatA.$model")
  .error(v-if="!$v.flatA.required") Field is required.
  .form-group(:class="{ 'form-group--error': $v.flatB.$error }")
    label.form__label Flat B
    input.form__input(v-model.trim="$v.flatB.$model")
  .error(v-if="!$v.flatB.required") Field is required.
  .form-group(:class="{ 'form-group--error': $v.forGroup.nested.$error }")
    label.form__label Nested field
    input.form__input(v-model.trim="$v.forGroup.nested.$model")
  .error(v-if="!$v.forGroup.nested.required") Field is required.
  .form-group(:class="{ 'form-group--error': $v.validationGroup.$error }")
  .error(v-if="$v.validationGroup.$error") Group is invalid.
  tree-view(:data="$v.validationGroup", :options="{rootObjectKey: '$v.validationGroup', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.flatA.$error }">
    <label class="form__label">Flat A</label>
    <input class="form__input" v-model.trim="$v.flatA.$model"/>
  </div>
  <div class="error" v-if="!$v.flatA.required">Field is required.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.flatB.$error }">
    <label class="form__label">Flat B</label>
    <input class="form__input" v-model.trim="$v.flatB.$model"/>
  </div>
  <div class="error" v-if="!$v.flatB.required">Field is required.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.forGroup.nested.$error }">
    <label class="form__label">Nested field</label>
    <input class="form__input" v-model.trim="$v.forGroup.nested.$model"/>
  </div>
  <div class="error" v-if="!$v.forGroup.nested.required">Field is required.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.validationGroup.$error }"></div>
  <div class="error" v-if="$v.validationGroup.$error">Group is invalid.</div>
  <tree-view :data="$v.validationGroup" :options="{rootObjectKey: '$v.validationGroup', maxDepth: 2}"></tree-view>
</div>

馆藏验证

带有$each关键字的数组支持

import { required, minLength } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      people: [
        {
          name: 'John'
        },
        {
          name: ''
        }
      ]
    }
  },
  validations: {
    people: {
      required,
      minLength: minLength(3),
      $each: {
        name: {
          required,
          minLength: minLength(2)
        }
      }
    }
  }
}
div
  div(v-for="(v, index) in $v.people.$each.$iter")
    .form-group(:class="{ 'form-group--error': v.$error }")
      label.form__label Name for {{ index }}
      input.form__input(v-model.trim="v.name.$model")
    .error(v-if="!v.name.required") Name is required.
    .error(v-if="!v.name.minLength")
      | Name must have at least {{ v.name.$params.minLength.min }} letters.
  div
    button.button(@click="people.push({name: ''})") Add
    button.button(@click="people.pop()") Remove
  .form-group(:class="{ 'form-group--error': $v.people.$error }")
  .error(v-if="!$v.people.minLength")
    | List must have at least {{ $v.people.$params.minLength.min }} elements.
  .error(v-else-if="!$v.people.required") List must not be empty.
  .error(v-else-if="$v.people.$error") List is invalid.
  button.button(@click="$v.people.$touch") $touch
  button.button(@click="$v.people.$reset") $reset
  tree-view(:data="$v.people", :options="{rootObjectKey: '$v.people', maxDepth: 2}")
<div>
  <div v-for="(v, index) in $v.people.$each.$iter">
    <div class="form-group" :class="{ 'form-group--error': v.$error }">
      <label class="form__label">Name for {{ index }}</label>
      <input class="form__input" v-model.trim="v.name.$model"/>
    </div>
    <div class="error" v-if="!v.name.required">Name is required.</div>
    <div class="error" v-if="!v.name.minLength">Name must have at least {{ v.name.$params.minLength.min }} letters.</div>
  </div>
  <div>
    <button class="button" @click="people.push({name: ''})">Add</button>
    <button class="button" @click="people.pop()">Remove</button>
  </div>
  <div class="form-group" :class="{ 'form-group--error': $v.people.$error }"></div>
  <div class="error" v-if="!$v.people.minLength">List must have at least {{ $v.people.$params.minLength.min }} elements.</div>
  <div class="error" v-else-if="!$v.people.required">List must not be empty.</div>
  <div class="error" v-else-if="$v.people.$error">List is invalid.</div>
  <button class="button" @click="$v.people.$touch">$touch</button>
  <button class="button" @click="$v.people.$reset">$reset</button>
  <tree-view :data="$v.people" :options="{rootObjectKey: '$v.people', maxDepth: 2}"></tree-view>
</div>

异步验证

开箱即用提供异步支持。只需使用返回承诺的验证器即可。Promise的成功值直接用于验证,失败的Promise只会使验证失败并抛出错误。

为了正确地做出反应,必须同步访问任何组件的数据。如果您需要在任何异步回调中使用它,请将其作为变量存储在验证程序的范围内,例如在中.then.

验证器会在每次数据更改时进行评估,因为它本质上是一个计算值。如果您需要限制异步调用,请在数据更改事件而不是在验证程序本身上进行。否则,您可能最终会破坏Vue的观测值。

import { required } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      username: ''
    }
  },
  validations: {
    username: {
      required,
      isUnique(value) {
        // standalone validator ideally should not assume a field is required
        if (value === '') return true
        // simulate async call, fail for all logins with even length
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(typeof value === 'string' && value.length % 2 !== 0)
          }, 350 + Math.random() * 300)
        })
      }
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.username.$error, 'form-group--loading': $v.username.$pending }")
    label.form__label Username
    input.form__input(v-model.trim="$v.username.$model")
  .error(v-if="!$v.username.required")
    | Username is required.
  .error(v-if="!$v.username.isUnique")
    | This username is already registered.
  tree-view(:data="$v.username", :options="{rootObjectKey: '$v.username', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.username.$error, 'form-group--loading': $v.username.$pending }">
    <label class="form__label">Username</label>
    <input class="form__input" v-model.trim="$v.username.$model"/>
  </div>
  <div class="error" v-if="!$v.username.required">Username is required.</div>
  <div class="error" v-if="!$v.username.isUnique">This username is already registered.</div>
  <tree-view :data="$v.username" :options="{rootObjectKey: '$v.username', maxDepth: 2}"></tree-view>
</div>

async/await语法完全支持。与Fetch API结合使用时效果特别好。

					
           
                    
  
validations:{async isUnique(value){if(value==='')return true const response=await fetch(`/api/unique/${value}`)return Boolean(await response.json())}}                    
					

延迟验证错误

无论您的要求多么奇特,您都可以使用$ touch状态来执行所需的任何操作。归结为在适当的时候调用$ touch和$ reset。例如,这是一个基于自定义setTimeout逻辑的易于遵循的延迟错误实现。最后一次输入后触发一秒钟。

import { required, minLength, maxLength } from 'vuelidate/lib/validators'
const touchMap = new WeakMap()
export default {
  data() {
    return {
      name: ''
    }
  },
  validations: {
    name: {
      required,
      minLength: minLength(4),
      maxLength: maxLength(15)
    }
  },
  methods: {
    delayTouch($v) {
      $v.$reset()
      if (touchMap.has($v)) {
        clearTimeout(touchMap.get($v))
      }
      touchMap.set($v, setTimeout($v.$touch, 1000))
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="name" @input="delayTouch($v.name)")
  .error(v-if="!$v.name.required") Field is required
  .error(v-if="!$v.name.minLength")
    | Name must have at least {{$v.name.$params.minLength.min}} letters.
  .error(v-if="!$v.name.maxLength")
    | Name must have at most {{$v.name.$params.maxLength.max}} letters.
  tree-view(:data="$v.name", :options="{rootObjectKey: '$v.name', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="name" @input="delayTouch($v.name)"/>
  </div>
  <div class="error" v-if="!$v.name.required">Field is required</div>
  <div class="error" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
  <div class="error" v-if="!$v.name.maxLength">Name must have at most {{$v.name.$params.maxLength.max}} letters.</div>
  <tree-view :data="$v.name" :options="{rootObjectKey: '$v.name', maxDepth: 2}"></tree-view>
</div>

访问验证器参数

您可以通过父验证器的$ params访问有关验证的信息。这对于向用户报告错误很有用。

import { required, minLength } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      form: {
        userName: '',
        password: ''
      }
    }
  },
  validations: {
    form: {
      userName: {
        required,
        minLength: minLength(5)
      },
      password: {
        required,
        minLength: minLength(8)
      }
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.form.userName.$error }")
    label.form__label Username
    input.form__input(v-model.trim="$v.form.userName.$model")
  .error(v-if="!$v.form.userName.required")
    | Field is required.
  .error(v-if="!$v.form.userName.minLength")
    | Field must have at least {{ $v.form.userName.$params.minLength.min }} characters.
  .form-group(:class="{ 'form-group--error': $v.form.password.$error }")
    label.form__label Password
    input.form__input(v-model.trim="$v.form.password.$model" type="password")
  .error(v-if="!$v.form.password.required")
    | Field is required.
  .error(v-if="!$v.form.password.minLength")
    | Field must have at least {{ $v.form.password.$params.minLength.min }} characters.
  .form-group(:class="{ 'form-group--error': $v.form.$error }")
    .error(v-if="$v.form.$error") Form is invalid.
  tree-view(:data="$v", :options="{rootObjectKey: '$v', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.form.userName.$error }">
    <label class="form__label">Username</label>
    <input class="form__input" v-model.trim="$v.form.userName.$model"/>
  </div>
  <div class="error" v-if="!$v.form.userName.required">Field is required.</div>
  <div class="error" v-if="!$v.form.userName.minLength">Field must have at least {{ $v.form.userName.$params.minLength.min }} characters.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.form.password.$error }">
    <label class="form__label">Password</label>
    <input class="form__input" v-model.trim="$v.form.password.$model" type="password"/>
  </div>
  <div class="error" v-if="!$v.form.password.required">Field is required.</div>
  <div class="error" v-if="!$v.form.password.minLength">Field must have at least {{ $v.form.password.$params.minLength.min }} characters.</div>
  <div class="form-group" :class="{ 'form-group--error': $v.form.$error }">
    <div class="error" v-if="$v.form.$error">Form is invalid.</div>
  </div>
  <tree-view :data="$v" :options="{rootObjectKey: '$v', maxDepth: 2}"></tree-view>
</div>

动态验证架构

验证模式可以是一个函数,它将使它成为动态的,并且可能取决于模型的数据。重新计算将自动进行,就像它是一个计算值一样。$dirty只要密钥名称不变或消失,验证的状态将被保留。

import { required } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      hasDescription: false,
      name: '',
      description: ''
    }
  },
  validations() {
    if (!this.hasDescription) {
      return {
        name: {
          required
        }
      }
    } else {
      return {
        name: {
          required
        },
        description: {
          required
        }
      }
    }
  }
}
div
  .form-group(:class="{ 'form-group--error': $v.name.$error}")
    label.form__label Name
    input.form__input(v-model.trim="$v.name.$model")
  .form-group
    label(for="hasDesc").form__label Has description?
    .toggle
      input#hasDesc(type="checkbox", v-model="hasDescription")
      label(for="hasDesc")
        .toggle__switch
  .form-group(v-if="hasDescription", :class="{ 'form-group--error': $v.description.$error}")
    label.form__label Description
    input.form__input(v-model.trim="$v.description.$model")
  tree-view(:data="$v", :options="{rootObjectKey: '$v', maxDepth: 2}")
<div>
  <div class="form-group" :class="{ 'form-group--error': $v.name.$error}">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="$v.name.$model"/>
  </div>
  <div class="form-group">
    <label class="form__label" for="hasDesc">Has description?</label>
    <div class="toggle">
      <input id="hasDesc" type="checkbox" v-model="hasDescription"/>
      <label for="hasDesc">
        <div class="toggle__switch"></div>
      </label>
    </div>
  </div>
  <div class="form-group" v-if="hasDescription" :class="{ 'form-group--error': $v.description.$error}">
    <label class="form__label">Description</label>
    <input class="form__input" v-model.trim="$v.description.$model"/>
  </div>
  <tree-view :data="$v" :options="{rootObjectKey: '$v', maxDepth: 2}"></tree-view>
</div>

动态参数

因为整个验证过程都是基于计算的属性,所以没有什么可以阻止您使验证者名称动态化。即使您的数据随时间变化,这种情况也允许动态行为。

import { minLength } from 'vuelidate/lib/validators'
export default {
  data() {
    return {
      name: '',
      minLength: 3,
      valName: 'validatorName'
    }
  },
  validations() {
    return {
      name: {
        [this.valName]: minLength(this.minLength)
      }
    }
  }
}
div
  .form-group
    label.form__label Validator name
    input.form__input(v-model.trim="valName" @input="$v.$touch()")
  .form-group
    label.form__label Dynamic min length
    input.form__input(type="number" v-model.number="minLength" @input="$v.$touch()")
  .form-group(:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="$v.name.$model")
  .error(v-if="!$v.name[valName]") Field is invalid
  tree-view(:data="$v", :options="{rootObjectKey: '$v', maxDepth: 2}")
<div>
  <div class="form-group">
    <label class="form__label">Validator name</label>
    <input class="form__input" v-model.trim="valName" @input="$v.$touch()"/>
  </div>
  <div class="form-group">
    <label class="form__label">Dynamic min length</label>
    <input class="form__input" type="number" v-model.number="minLength" @input="$v.$touch()"/>
  </div>
  <div class="form-group" :class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="$v.name.$model"/>
  </div>
  <div class="error" v-if="!$v.name[valName]">Field is invalid</div>
  <tree-view :data="$v" :options="{rootObjectKey: '$v', maxDepth: 2}"></tree-view>
</div>

API


vuelidate中 存在两种不同的结构:vuelidate:

  • validations 组件选项-验证的定义
  • $v 结构-视图模型中保存验证状态的对象

$v值

$v 模型代表验证的当前状态。它通过在validations选项结构之后定义一组属性来保持用户定义的验证功能的输出,从而做到这一点。这些特殊的保留关键字的存在意味着您不能使用该名称指定自己的验证器。

名称 类型 描述
$invalid boolean 指示给定模型的验证状态。成为true 当任何选项中指定其子验证的返回falsy 值。对于验证组,将考虑所有分组的验证器。
$dirty boolean 一个标志,表示验证的字段是否被用户至少触摸了一次。通常,它用于确定是否应该将消息显示给最终用户。您可以使用 $touch$reset方法手动管理此标志。写入$model值时会自动设置。该$dirty标志被认为是 true 如果给定的模型被$touchall of it's children are $dirty.
$anyDirty boolean 一个非常类似于的标志$dirty,但有一个例外。该$anyDirty标志被认为是true 如果给定的模型被$touch编或any of it's children are $anyDirty.
$model any 对原始验证模型的引用。读取该值将始终为您提供与直接引用该模型完全相同的值。这意味着this.$v.value.$model 相当于this.value读时。写入该值将$touch 自动更新模型并调用方法。用作v-model 有效负载非常有用,它提供了一种自动将给定字段标记为$dirty第一次触摸的方式。与.lazy 修饰符搭配良好。
$error boolean 方便标记,可轻松决定是否显示消息。等同于this.$dirty && !this.$pending && this.$invalid.
$anyError boolean 方便标记,可轻松决定是否显示消息。一种变体,当其自身或 $error 等于时,认为会显示错误 true.
$pending boolean 指示当前是否有任何子异步验证程序待处理。false如果所有验证器都是同步的,则始终为。
$params object 包含当前级别上所有提供的验证器的类型和参数,以及子验证组的类型和参数,可以使用withParams进行声明。用作错误呈现系统的输入。在翻译后的文本中使用安全。
$each object 包含集合验证器的所有验证模型。始终保留原始模型的密钥,但也保留所有关联的验证器的其他名称和特殊值,例如$invalid。指令$iter 内部最好使用特殊字段v-for.
$iter object 仅作为$each对象的直接子对象出现。拥有集合验证器的所有验证模型,仅此而已。可以在v-for 循环中对所有模型验证器进行迭代时安全地引用它。
有关使用示例,请参见集合验证。

$v 方法

一组控制验证模型的方法。在每个嵌套级别均可访问。所有方法都可以在所需的任何事件处理程序上使用。没有额外的语法来决定何时设置脏标志。只需使用标准@input@blur 绑定。

名称 描述
$touch $dirty 将模型及其所有子级 的标志设置为true 递归。
$reset $dirty 将模型及其所有子级 的标志设置为 false递归。
$flattenParams 返回叶参数数组。

配置关键字

名称 类型 描述
$each object 嵌套验证的定义分别应用于给定模型的每个道具。非常适合验证数组,但可以与任何对象或集合一起使用。
$trackBy string || func 必须是的直接子代$each,但是可选的。定义对象属性的访问器,通过该访问器$each跟踪其子模型。必须正确保留$dirty随机插入的标志。如果未预置此属性,则使用该键进行跟踪。

验证者


vuelidate附带了一组内置的验证器,您可以只需要和使用它们,但并不仅限于此。所有这些都只是简单的谓词-到中的数据功能boolean,表示数据是否有效。您可以轻松地编写自己的或使用任何功能在这个形状从你已经有了,像任何图书馆_.conformsTo 从lodash或高阶函数和像链 R.cond 从ramda。考虑可能性。

本文档为每个内置验证器提供了简短描述,并提供了一个示例自定义验证器实现,以帮助理解它们并尽可能轻松地编写您自己的验证器。

内置验证器

要使用任何内置的验证器,您必须从vuelidate库中导入它。

import { required, maxLength } from 'vuelidate/lib/validators'

您还可以直接导入特定的验证器,以免在捆绑器不支持摇树的情况下加载未使用的验证器。Rollup或Webpack 2等不需要此功能。

import required from 'vuelidate/lib/validators/required'
import maxLength from 'vuelidate/lib/validators/maxLength'
							

通过使用支持浏览器的捆绑包,可以直接在浏览器中使用验证器。请记住,这将始终一次加载所有内置的验证器。

<script src="vuelidate/dist/validators.min.js"></script>
var required = validators.required
var maxLength = validators.maxLength

这是提供的验证器的完整列表。

名称 参数 描述
required none 需要非空数据。检查空数组和仅包含空格的字符串。
requiredIf locator * 仅在提供的属性或谓词为true时才需要非空数据。
requiredUnless locator * 仅在提供的属性或谓词为false时才需要非空数据。
minLength min length 要求输入具有指定的最小长度(含在内)。与数组一起使用。
maxLength max length 要求输入具有最大的指定长度(包括该长度)。与数组一起使用。
minValue min 要求输入具有指定的最小数值或日期。
maxValue max 要求输入具有指定的最大数值或日期。
between min, max 检查数字或日期是否在指定范围内。最小值和最大值都包括在内。
alpha none 仅接受字母字符。
alphaNum none 仅接受字母数字。
numeric none 仅接受数字。
integer none 接受正负整数。
decimal none 接受正负十进制数。
email none 接受有效的电子邮件地址。请记住,您仍然必须在服务器上仔细验证它,因为如果不发送验证电子邮件就无法确定该地址是否真实。
ipAddress none 接受点分十进制表示法的有效IPv4地址,例如127.0.0.1。
macAddress separator=':' 接受有效的MAC地址,例如00:ff:11:22:33:44:55。不要忘记调用它macAddress()因为它具有可选参数。您可以指定自己的分隔符,而不是':'。提供空的分隔符macAddress('')以验证MAC地址,例如00ff1122334455。
sameAs locator * 检查给定属性是否相等。
url none 仅接受网址。
or validators... 当提供的验证器中的至少一个通过时通过。
and validators... 当所有提供的验证程序通过时通过。
not validator 如果提供的验证程序不通过则通过,否则失败。可以与其他验证器(如)链接在一起not(sameAs('field')).
withParams $params, validator 不是真正的验证器,而是验证器修饰符。将$params 对象添加到提供的验证器。可以用于验证功能,甚至可以用于整个嵌套字段验证对象。对于创建自己的自定义验证器很有用。

定位符可以是同级属性名称或函数。当作为函数提供时,它将在验证下接受模型作为参数并this绑定到组件实例,因此即使在嵌套验证的范围内,您也可以访问其所有属性和方法。

使用定位器元参数的条件验证示例:

export default {
  ...,
  data() {
    return {
      field: "foo",
      nested: {
        field: "bar",
        someFlag: true
      }
    }
  },
  computed: {
    isOptional() {
      return true // some conditional logic here...
    }
  },
  validations: {
    field: {
      required: requiredUnless('isOptional')
    },
    nested: {
      required: requiredIf(function (nestedModel) {
        return !this.isOptional && nestedModel.someFlag
      })
    }
  }
}

验证器参数

每个验证器都可以保存参数。验证器负责保存其类型和参数,因为它们是简单的函数,我们可能希望将其告知用户。

使用withParams到的参数适用于验证。声明的参数上升一级,因此它们包含在$params 父验证对象的中。Vuelidate的设计不允许验证结果直接包含参数。

您可以$flattenParams 在验证对象上调用方法,以获取该验证对象中存在的所有验证器的验证器参数数组。例如,假设一个验证对象包含一个between验证器,以检查值是否在5到10之间。调用$flattenParams 将返回以下数组。

[{ path: [], name: 'between', params: { type: 'between', min: 5, max: 10 } }]

定制验证器


您可以轻松编写自定义验证器,并将它们与内置验证器结合使用,因为它们只是一个简单的谓词函数。

最简单的例子

假设您需要一个验证器来检查字符串中是否包含很酷的子字符串。解决此问题的方法是使用检查该错误的常规javascript函数。

const mustBeCool = (value) => value.indexOf('cool') >= 0

第二部分实际上是应用验证器。您可以执行与内置方法完全相同的方法。

validations: {
  myField: {
    required,
    mustBeCool
  }
}

可选验证器

上面介绍的模式通常足够好,但是此验证器将始终返回false 空输入。当您的输入被认为是可选的时,这是不正确的。因此,存在一个req帮助程序,它是所需验证程序的简化版本。您可以使用它来使您的验证器在可选字段(即没有required 验证器的字段)存在的情况下表现良好。

import { helpers } from 'vuelidate/lib/validators'
const mustBeCool = (value) => !helpers.req(value) || value.indexOf('cool') >= 0
// ...
validations: {
  myField: {
    mustBeCool
  }
}

额外参数

如果您的验证器需要提供参数,则可以简单地创建一个返回实际验证器的高阶函数,例如between内置验证器。

import { helpers } from 'vuelidate/lib/validators'
const contains = (param) =>
  (value) => !helpers.req(value) || value.indexOf(param) >= 0
// ...
validations: {
  myField: {
    mustBeCool: contains('cool')
  }
}

$props 支持

如果您不使用$props 属性功能(例如在翻译系统中),那么就可以了。为了使您的验证器也能产生一些有用的信息$props,您可以使用withParamshelper。最简单的情况是仅添加 type元数据,这可能对以后选择正确的翻译字符串很有用。

import { helpers } from 'vuelidate/lib/validators'
const mustBeCool = helpers.withParams(
  { type: 'mustBeCool' },
  (value) => !helpers.req(value) || value.indexOf('cool') >= 0
)
// ...
console.log(this.$v.myField.$params.mustBeCool)
// -> { type: 'mustBeCool' }

相同的行为扩展到具有更多参数的高阶验证器。您只需要注意用调用将内部函数包装起来withParams ,如下所示。

import { helpers } from 'vuelidate/lib/validators'
const contains = (param) =>
  helpers.withParams(
    { type: 'contains', value: param },
    (value) => !helpers.req(value) || value.indexOf(param) >= 0
  )
// ...
validations: {
  myField: {
    mustBeCool: contains('cool')
  }
}
// ...
console.log(this.$v.myField.$params.mustBeCool)
// -> { type: 'contains', value: 'cool' }

访问组件

I在需要访问整个模型的更复杂的情况下,例如sameAs,请使用函数context(this)访问组件上的任何值或使用提供parentVm的访问同级属性。


 // both equivalent
const otherFieldContainsMe =
  (value, vm) => vm.other.nested.field.contains(value)
function otherFieldContainsMe (value) {
  return this.other.nested.field.contains(value)
}

基于正则表达式的验证器

一些验证器可以很容易地表达为正则表达式。您可以使用regex助手来快速定义这种成熟的验证器。这已经包括处理可选字段和$params.


import { helpers } from 'vuelidate/lib/validators'
const alpha = helpers.regex('alpha', /^[a-zA-Z]*$/)

基于定位器的验证器

如果要使用定位器策略,该策略与insameAsrequiredIf内置验证器完全相同,则可以使用ref helper来完成此工作,方法与在这两个验证器中使用的方法完全相同。


import { ref, withParams } from './common'
export default (equalTo) =>
  withParams({ type: 'sameAs', eq: equalTo }, function(value, parentVm) {
    return value === ref(equalTo, this, parentVm)
  })
import { req, ref, withParams } from './common'
export default (prop) =>
  withParams({ type: 'requiredIf', prop }, function(value, parentVm) {
    return ref(prop, this, parentVm) ? req(value) : true
  })

请注意,导入略有不同,因为从库源的角度来看,这就是代码的样子。如果您愿意贡献自己的验证者进行审核,则此样式是正确的样式。您仍应helpers在自己的代码内使用export(如前面的示例所示)。

助手列表

该表包含可用于帮助您编写自己的验证器的所有帮助器。您可以从验证程序库中导入它们

import { helpers } from 'vuelidate/lib/validators'
Helper 描述
withParams 允许将$params元数据添加到您的验证功能。
req required验证器的 最低版本。用它来使您的验证器接受可选字段
ref 定位助手。这样可以方便地引用模型中的其他字段。
len 获取任何类型的值的长度,无论在上下文中什么都有意义。这可能意味着数组长度,字符串长度或对象上的键数
regex 用于快速创建基于正则表达式的验证器。

Copyright © 2019 www.vuelidate.cn vuelidate中文网 版权所有 [英文站]