# 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) } ```