# vee-validate
{%hackmd BJrTq20hE %}
表單驗證,目前只有Vee 4.0支援 Vue3
[https://hackmd.io/FFv0a5cBToOATP7uI5COMQ](https://hackmd.io/FFv0a5cBToOATP7uI5COMQ)
線上測試: [https://vee-validate.logaretm.com/v4/guide/global-validators#available-rules](https://vee-validate.logaretm.com/v4/guide/global-validators#available-rules)
## CDN版本
### 1. CDN
```javascript=
<script src="https://cdnjs.cloudflare.com/ajax/libs/vee-validate/4.1.17/vee-validate.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@vee-validate/i18n@4.1.17/dist/vee-validate-i18n.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@vee-validate/rules@4.1.17/dist/vee-validate-rules.min.js"></script>
```
### 2. 註冊元件
```javascript=
const app = Vue.createApp({
// ...
});
app.component('VForm', VeeValidate.Form); //form表單
app.component('VField', VeeValidate.Field); //input欄位
app.component('ErrorMessage', VeeValidate.ErrorMessage); //錯誤訊息
app.mount('#app');
```
### 3. 定義規則
選擇加入特定規則,全規則可參考
```javascript=
VeeValidate.defineRule('email', VeeValidateRules['email']);
VeeValidate.defineRule('required', VeeValidateRules['required']);
```
全部加入:
```javascript=
Object.keys(VeeValidateRules).forEach(rule => {
ValidateRules[rule]);
});
```
全部加入(CDN 版本):
```javascript=
Object.keys(VeeValidateRules).forEach(rule => {
if (rule !== 'default') {
VeeValidate.defineRule(rule, VeeValidateRules[rule]);
}
});
```
### 4. 加入多國語系
將外部資源儲存至本地(JSON中文檔) [https://github.com/logaretm/vee-validate/blob/vee-validate%404.1.16/packages/i18n/src/locale/zh_TW.json](https://github.com/logaretm/vee-validate/blob/vee-validate%404.1.16/packages/i18n/src/locale/zh_TW.json)
```javascript=
VeeValidateI18n.loadLocaleFromURL('./zh_TW.json');
// Activate the locale
VeeValidate.configure({
generateMessage: VeeValidateI18n.localize('zh_TW'),
validateOnInput: true, // 調整為輸入字元立即進行驗證
});
```
### 5. 套用 v-form 並加入 v-slot
```javascript=
<v-form v-slot="{ errors }" @submit="onSubmit" >
//備註:v-slot 稱為插槽 Props,可以將驗證結果的回饋資料直接帶入於區塊中
```
### 6. 套用 v-field 及 error-message(此為搭配bootstrap validation)
常用技巧:
- name 為必填,是錯誤驗證的回饋欄位,會與多個項目進行匹配(errors, errors-message…)
- 為 v-field 帶入全域設定的規則,可參考相關文件
- as 可以改變 v-field 的型態,如以下 select 範例
- :class 可運用 v-form 帶入的驗證錯誤作為判斷
```javascript=
<v-field id="email" name="email" type="email" class="form-control" :class="{ 'is-invalid': errors['email'] }" placeholder="請輸入 Email" rules="email|required" v-model="user.email"></v-field>
<error-message name="email" class="invalid-feedback"></error-message>
```
Select 範例
```javascript=
<v-field id="name" name="地區" class="form-control" :class="{ 'is-invalid': errors['地區'] }"
placeholder="請輸入地區" rules="required" v-model="user.region" as="select">
<option value="">請選擇地區</option>
<option value="台北市">台北市</option>
<option value="高雄市">高雄市</option>
</v-field>
```
### 7. 加入自訂驗證、送出表單等行為…
範例:自訂驗證
rules 中可自訂函式來驗證結果:
```javascript=
<div class="mb-3">
<label for="address" class="form-label">電話</label>
<v-field id="address" name="電話" type="text" class="form-control" :class="{ 'is-invalid': errors['電話'] }"
placeholder="請輸入電話" :rules="isPhone" v-model="user.phone"></v-field>
<error-message name="電話" class="invalid-feedback"></error-message>
</div>
methods: {
isPhone(value) {
const phoneNumber = /^(09)[0-9]{8}$/
return phoneNumber.test(value) ? true : '需要正確的電話號碼'
}
}
```
範例:送出表單:
```javascript=
<v-form v-slot="{ errors }" @submit="onSubmit" >
...
<button class="btn btn-primary" type="submit">Submit</button>
</v-form>
methods: {
onSubmit() {
// ...
},
},
```
Form reset:
`this.$refs.form.resetForm();`
## Vue Cli版本:
### 步驟一:安裝veeValidate套件,一樣有主套件、規則套件、多國語言套件
```javascript=
npm install vee-validate@next @vee-validate/rules @vee-validate/i18n --save
```
### 步驟二:在進入點( main.js )匯入套件,PS:需在import { createApp } from "vue";之後
```javascript=
// 匯入 vee-validate 特定規則(與匯入全部規則擇一)
import { required, email, min } from "@vee-validate/rules";
```
```javascript=
//匯入 vee-validate 主套件
import { Field, Form, ErrorMessage, defineRule, configure } from "vee-validate";
//匯入全部規則:
import AllRules from '@vee-validate/rules'
//匯入多國語系的功能
import { localize, setLocale } from "@vee-validate/i18n";
//匯入繁體中文語系檔案
import zhTW from "@vee-validate/i18n/dist/locale/zh_TW.json";
```
### 步驟三:定義驗證規則(in main.js)
```javascript=
//定義驗證規則
defineRule("required", required);
defineRule("email", email);
defineRule("min", min);
//匯入全部規則
Object.keys(AllRules).forEach((rule) => {
defineRule(rule, AllRules[rule]);
});
//設定 vee-validate 全域規則
configure({
generateMessage: localize({ zh_TW: zhTW }), //載入繁體中文語系
validateOnInput: true //當輸入任何內容直接進行驗證
});
//設定預設語系
setLocale("zh_TW");
```
### 步驟四:註冊元件(in main.js)
```javascript=
app.component('Form', Form);
app.component('Field', Field);
app.component('ErrorMessage', ErrorMessage);
```
測試code:在views裡面(三個name相等,`name="email"`、`errors['email']`、`name="email"`,缺一不可)
```htmlembedded=
<template>
<div class="home">
<Form v-slot="{ errors }" @submit="onSubmit">
{{ errors }} {{ values }}
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<Field
id="email"
name="email"
type="email"
class="form-control"
:class="{ 'is-invalid': errors['email'], 'is-valid': form.user.email }"
placeholder="請輸入 Email"
rules="email|required"
v-model="user.email"
></Field>
<ErrorMessage name="email" class="invalid-feedback"></ErrorMessage>
</div>
<button class="btn me-2 btn-outline-primary" type="button" @click="validate">
驗證
</button>
<button class="btn btn-primary" type="submit">Submit</button>
</Form>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
user: {},
};
},
methods: {
onSubmit() {
console.log(this.user);
},
},
created() {
console.log(this);
},
};
</script>
```
可以使用computed來監視表單有無空值:
```htmlembedded=
<button type="submit" class="btn btn-chocolight py-2 px-5" :disabled="!checkData">
送出訂單
</button>
```
```javascript=
data() {
form: {
user: {
name: '',
email: '',
tel: '',
address: '',
},
message: '',
},
computed: {
checkData() {
const attrs = ['name', 'email', 'tel', 'address'];
return attrs.every((item) => this.form.user[item] !== '');
},
},
```
## [二次密碼驗證](https://vee-validate.logaretm.com/v4/guide/global-validators#confirmed)
```htmlembedded=
//confirmed:@password
<Form>
<Field name="password" type="password" />
<Field name="confirmation" type="password" rules="confirmed:@password" />
</Form>
```
## composition版本:[官網](https://vee-validate.logaretm.com/v4/guide/composition-api/validation)
1. 安裝
```javascript=
npm i vee-validate@next --save
```
1. 基本結構
- useField:input包裝器
```javascript=
<template>
<div>
<input v-model="value" /> // 綁定useFeild提供的value
<span>{{ errorMessage }}</span>
</div>
</template>
<script setup>
import { useField } from 'vee-validate';
// 參數1:'name'為欄位名稱
// 參數2:驗證函式
const { value, errorMessage } = useField('name', inputValue => !!inputValue);
</script>
```
- useForm:表單包裝器(大量表單用)
```htmlembedded=
<template>
<form @submit="submit">
<BaseRadioGroup
name="pets"
:options="[
{ value: 1, label: 'Yes' },
{ value: 0, label: 'No' }
]"
v-model="pets"
:error="errors.pets"
/>
...
</form>
</template>
```
```javascript=
import { useField, useForm } from 'vee-validate'
export default {
setup () {
...
// 自訂驗證
const validationSchema = {
category: required,
title: value => {
const req = required(value)
if (req !== true) return req
const min = minLength(3, value)
if (min !== true) return min
return true
},
description: required,
location: undefined,
pets: anything,
catering: anything,
music: anything
}
// handleSubmit:取出表單驗證方法
// errors:包含所有表單回傳的錯誤
const { handleSubmit, errors } = useForm({
validationSchema, // 條件判斷包裝
initialValues: { // 初始預設值
pets: 1,
catering: false,
music: false
}
})
// 從表單們中取值
const { value: pets } = useField('pets')
...
const submit = handleSubmit(values => {
console.log('submit', values)
})
return {
...,
errors
}
}
}
```
### [yup](https://www.npmjs.com/package/yup)
- 使用yup風格validate:
```javascript=
import * as yup from 'yup' // (X)匯入所有
const validationSchema = object({
category: ypu.string().required(),
...
})
```
```javascript=
import { object, string, number, boolean } from 'yup' //使用解構賦值
const validationSchema = object({
category: string().required(),
title: string().required('A cool title is required').min(3),
description: string().required(),
location: string(),
pets: number(),
catering: boolean(),
music: boolean()
})
```
### lazy validation:延遲驗證(進階)
-
ps:此範例為搭配`BaseInput`,不使用v-model雙向綁定資料了,而是間接用onChange來改變value值(單向)
```h=
<template>
<h1>Create an Event</h1>
<form @submit="submit">
<BaseInput
label="Email"
type="email"
:error="emailError"
:modelValue="email"
@change="handleChange"
/>
</form>
</template>
```
```javascript=
const { setFieldValue } = useForm({
validationSchema: validations
})
const { value: email, errorMessage: emailError } = useField('email')
const handleChange = (event) => {
// Q:為何不直接修改變數? A:因為直接修改就不會觸發驗證了
setFieldValue('email', event.target.value)
}
```