# 🏅 Day 21 - VeeValidate 表單驗證套件運用 ## 今日學習目標 - 學習將 VeeValidate 整合至 Nuxt3 - 學習如何在 Nuxt3 中使用 VeeValidate 的元件、驗證規則以及多國語系功能 ## **安裝 VeeValidate** 在 Nuxt3 中使用 **VeeValidate** 進行表單驗證,需要安裝以下三個套件: - `@vee-validate/nuxt` : 提供與 Nuxt 整合的功能,包含自動匯入元件和組合函式(composables)。 - `@vee-validate/rules`:提供內建的驗證規則,如必填、Email 格式等。 - `@vee-validate/i18n` :提供驗證訊息多國語系的支援。 ### **安裝** @vee-validate/nuxt VeeValidate 官方提供了支援 Nuxt 的 [**@vee-validate/nuxt** ](https://vee-validate.logaretm.com/v4/integrations/nuxt/) 模組,可以自動匯入 VeeValidate 的元件(如 `Form`、`Field`)及組合函式 (composables)。可以在終端機輸入以下指令來安裝 : ```bash npm install @vee-validate/nuxt ``` ### 安裝注意事項 `@vee-validate/nuxt` 在 Nuxt 最新的 3.13.2 版本會遇到無法自動匯入元件的問題,發生 `The requested module '/_nuxt/node_modules/vee-validate/dist/vee-validate.js' does not provide an export named 'Field’` 錯誤,如下圖。 ![day21-1](https://hackmd.io/_uploads/rJqh26YMkg.png) 根據 [GitHub Issue](https://github.com/logaretm/vee-validate/issues/4862) 的討論,此問題不會在 Nuxt 3.12.4 版本發生。建議執行以下步驟將 Nuxt 版本調整至 3.12.4 : 1. 修改 `package.json` dependencies ,將 `nuxt` 版本限定為 3.12.4: ```bash { ... 其他設定 "dependencies": { "nuxt": "3.12.4", }, ... 其他設定 } ``` 2. 刪除 `package-lock.json` 和 `node_modules` 資料夾並執行 `npm install` 重新安裝。 3. 安裝完成後,檢查 `package-lock.json` 確認 Nuxt 版本是否為 3.12.4 ( 如下圖 ) 。 ![day21-2](https://hackmd.io/_uploads/B12xTptMyl.png) ### 安裝 @vee-validate/rules 與 @vee-validate/i18n 為了使用驗證規則與多國語系功能,還需安裝 `@vee-validate/rules` 和 `@vee-validate/i18n`: ```bash npm install @vee-validate/rules @vee-validate/i18n ``` ## 設定 @vee-validate/nuxt 安裝後,需要在 `nuxt.config.ts` 將 `@vee-validate/nuxt` 模組加入到 `modules` 屬性,啟用 VeeValidate 的元件和功能。 ```tsx // nuxt.config.ts export default defineNuxtConfig({ // ... 其他設定 modules: ["@vee-validate/nuxt"], }); ``` ### 自訂元件名稱 VeeValidate 會自動匯入預設的 `Form`、`Field` 和 `ErrorMessage` 元件。若希望修改這些元件的名稱可以在 `nuxt.config.ts` 中加入 `veeValidate.componentNames` 屬性。例如下方的調整,分別將 `Form`、`Field` 和`ErrorMessage` 的名稱修改為 `VForm`、`VField` 和`VErrorMessage` 。 ```jsx // nuxt.config.ts export default defineNuxtConfig({ // ... 其他設定 modules: ["@vee-validate/nuxt"], veeValidate: { // 修改 VeeValidate 元件的名稱 componentNames: { Form: "VForm", Field: "VField", ErrorMessage: "VErrorMessage", }, }, }) ``` ### 關閉自動匯入 若不需要自動匯入 VeeValidate 的元件和組合函式 (composables),可以將 `autoImports` 設為 `false`: ```jsx // nuxt.config.ts export default defineNuxtConfig({ // ... 其他設定 modules: ["@vee-validate/nuxt"], veeValidate: { // autoImports 預設為 true ( 開啟自動匯入) // 調整為 false 會關閉自動匯入。 autoImports: false, }, }) ``` ## 全域引入驗證規則與多國語系 `@vee-validate/nuxt` 模組不包含驗證規則與多國語系功能,需要透過 Nuxt 的插件系統來全域導入這些功能。以下將逐步說明在 Nuxt3 中全域設定 **VeeValidate** 的驗證規則與多國語系。 ### 步驟一. 建立插件 使用 `npx nuxi add plugin vee-validate.js` 指令或手動建立 `plugins/vee-validate.js` 檔案,並將 `defineNuxtPlugin()` 預設匯出。 ```jsx // plugins/vee-validate.js export default defineNuxtPlugin((nuxtApp) => { // 插件的設定 }); ``` ### 步驟二.全域匯入驗證規則 從 [@vee-validate/rules](https://vee-validate.logaretm.com/v4/guide/global-validators#vee-validaterules) 匯入驗證規則,並使用 `defineRule` 定義全域規則。例如使用 `defineRule` 匯入 `required` 規則 : ```jsx // plugins/vee-validate.js import { defineRule } from "vee-validate"; import { required } from "@vee-validate/rules"; export default defineNuxtPlugin((nuxtApp) => { // 定義全域的規則 defineRule("required", required); }); ``` 若需要自訂驗證規則,同樣是在插件使用 `defineRule` 定義。例如自訂 `username` 規則驗證使用者名稱,要求 3 至 15 個字元,且只能包含英文字母、數字與底線 : ```jsx // plugins/vee-validate.js import { defineRule } from "vee-validate"; import { required } from "@vee-validate/rules"; export default defineNuxtPlugin((nuxtApp) => { // 定義全域的規則 defineRule("required", required);= // 自訂驗證規則 defineRule("username", (value) => { const regex = /^[a-zA-Z0-9_]{3,15}$/; return ( regex.test(value) || "使用者名稱只能包含字母、數字與底線,且長度須為 3 至 15 字元" ); }); }); ``` ### 步驟三. 設定多國語系 (i18n) 從 [@vee-validate/i18n](https://vee-validate.logaretm.com/v4/guide/i18n#using-vee-validatei18n) 匯入 `localize` 和 `setLocale` 函式,並設定繁體中文的驗證訊息 : ```jsx // plugins/vee-validate.js import { defineRule, configure } from "vee-validate"; import { required } from "@vee-validate/rules"; import { localize, setLocale } from "@vee-validate/i18n"; import zhTW from "@vee-validate/i18n/dist/locale/zh_TW.json"; export default defineNuxtPlugin((nuxtApp) => { // ...省略驗證規則的設定 // 設定多國語系與驗證訊息 configure({ // 載入繁體中文的設定檔,產生繁體中文的驗證訊息 generateMessage: localize({ zh_TW: zhTW }), validateOnInput: true, // 輸入文字時立即進行驗證 }); // 設定預設語言為繁體中文 setLocale("zh_TW"); }); ``` ## 使用 **VeeValidate 元件在 Nuxt 進行表單驗證** 完成 VeeValidate 的安裝與設定後,我們可以開始在 Nuxt 中實作表單驗證。以下將逐步說明如何使用 VeeValidate 元件,設定驗證規則並處理表單提交流程。 ### 步驟一. 使用 VForm 元件 在表單的最外層使用 [VForm 元件](https://vee-validate.logaretm.com/v4/api/form#form-component),預設會渲染成 `<form>` 元素。透過 [v-slot](https://vee-validate.logaretm.com/v4/api/form#slots) 插槽,VForm 元件可以獲取表單的驗證結果與操作方法,例如: - `errors` : 取得表單各欄位的驗證錯誤訊息。 - `meta.valid`:檢查所有欄位的驗證狀態。當所有欄位驗證通過,該值為 `true`;否則為 `false`。可以用來控制送出按鈕的啟用狀態。 - `resetForm`: 用來重置表單狀態的方法。 ```html <VForm v-slot="{ errors, meta, resetForm }"> <!-- 其他表單元素 --> <!-- 使用 v-slot 取出的 resetForm 方法重置表單 --> <button class="btn btn-outline-warning" type="button" @click="resetForm"> 重新選擇 </button> <!-- :disabled 屬性綁定從 v-slot 取出的 meta.valid ,檢查欄位的驗證是否有錯誤 --> <button class="btn btn-primary" type="submit" :disabled="!meta.valid"> 送出 </button> </VForm> ``` > 💡 補充 : 重置表單狀態另一種作法可以在 @submit 事件函式的第二個參數取出 resetForm : ```html <script setup> const onSubmit = (value, { resetForm }) => { // ...執行其他程式 // 可以從第二個參數取出 resetForm 方法來重置表單 resetForm(); }; </script> <VForm v-slot="{ errors, meta, resetForm } @submit="onSubmit"> <!-- 其他表單元素 --> </VForm> ``` ### 步驟二. 使用 VField 與 VErrorMessage 元件 在 VForm 元件內使用 [VField](https://vee-validate.logaretm.com/v4/api/field#rendering-simple-fields-with-as-prop) [](https://vee-validate.logaretm.com/v4/guide/components/validation#using-errormessage-component)元件來渲染表單的輸入欄位,預設會渲染成 `<input>` 元素。經常使用的屬性包括: - `name`:定義欄位名稱,用於驗證訊息的對應。例如下方的使用者名稱欄位, `name="username"` 定義了名稱為 `username` 的欄位,驗證失敗會將錯誤訊息存入 `errors.username` 。 - `rules`:指定欄位的驗證規則,規則之間使用 `|` 分隔。例如下方使用者名稱欄位的 `rules="required|username"` 使用了在 `plugins/vee-validate.js` 中定義的 `required` 和 `username` 規則。 - `:class` : 可以根據欄位的驗證錯誤狀態動態套用樣式,例如 `:class="{ 'is-invalid': errors['username'] }"` 會在 `username` 欄位驗證失敗時添加 `is-invalid` 樣式。 ```html <VForm v-slot="{ errors }"> <label class="form-label" for="username">使用者名稱</label> <VField id="username" name="username" <-- 欄位的名稱為 username ,驗證失敗時會對應到此名稱,把錯誤訊息寫入 errors.username class="form-control" :class="{ 'is-invalid': errors['username'] }" <-- 將錯誤訊息取出並套用樣式 type="text" rules="required|username" <--使用全域的 required 和 username 規則 /> </VForm> ``` - `as` : 改變 VField 元件要渲染的元素。例如使用 `as="textarea"` 可以將 `VField` 渲染成 `<textarea>` 元素,適用於多行文字的輸入區域: ```html <VForm v-slot="{ errors }"> <label class="form-label" for="userDescription">使用者簡介</label> <VField id="userDescription" name="userDescription" class="form-control" :class="{ 'is-invalid': errors['userDescription'] }" placeholder="請輸入您的簡短自我介紹" rules="required" as="textarea" <-- 指定渲染成 <textarea> 元素 /> </VForm> ``` 除此之外,為了顯示驗證錯誤訊息,可以在 VForm 元件內使用 [VErrorMessage](https://vee-validate.logaretm.com/v4/guide/components/validation#using-errormessage-component) 元件,預設會渲染成 `<span>`元素。必需設定的屬性包括 : - `name` : name 屬性的值必需與對應 `VField` 元件的 `name` 屬性一致,才能正確顯示該欄位的錯誤訊息。 ```html <VForm v-slot="{ errors }"> <div class="mb-3"> <label class="form-label" for="username">使用者名稱</label> <VField id="username" name="username" class="form-control" :class="{ 'is-invalid': errors['username'] }" type="text" rules="required|username" /> <!-- name 屬性的值必需與 VField 元件 name 屬性的值 ( username ) 一致 --> <VErrorMessage class="invalid-feedback" name="username" /> </div> <div class="mb-3"> <label class="form-label" for="userDescription">使用者簡介</label> <VField id="userDescription" name="userDescription" class="form-control" :class="{ 'is-invalid': errors['userDescription'] }" placeholder="請輸入您的簡短自我介紹" rules="required" as="textarea" /> <!-- name 屬性的值必需與 VField 元件 name 屬性的值 ( userDescription ) 一致 --> <VErrorMessage class="invalid-feedback" name="userDescription" /> </div> </VForm> ``` ### 步驟三. 提交表單 最後,可以在 VForm 元件使用 `@submit` 事件處理提交邏輯。因為 VForm 元件觸發 `@submit` 事件會自動調用 `event.preventDefault()`,所以不需要額外使用 `.prevent` 修飾符。除此之外,表單提交時會自動將驗證後的資料傳遞給事件函式,不需要在每個 VField 欄位綁定 `v-model` ,例如: ```html <script setup> const onSubmit = (value, { resetForm }) => { console.log("表單提交的值", value); // 可以從第二個參數取出 resetForm 方法來重置表單 resetForm(); }; </script> <template> <!-- 1. @submit 事件不需加入 .prevent 修飾符 --> <VForm v-slot="{ errors, meta, resetForm }" @submit="onSubmit"> <!-- 2. 不需要在 VField 綁定 v-model,表單提交時會自動將值傳遞給 submit 函式 --> <VField id="username" name="username" class="form-control" :class="{ 'is-invalid': errors['username'] }" type="text" rules="required|username" /> <VErrorMessage class="invalid-feedback" name="username" /> <button class="btn btn-outline-warning" type="button" @click="resetForm"> 重置 </button> <button class="btn btn-primary" type="submit" :disabled="!meta.valid"> 送出 </button> </VForm> </template> ``` <br> > 今日學習的[範例 Code - 資料夾: day21-vee-validate-example](https://github.com/hexschool/nuxt-daily-tasks-2024) ## 題目 請 fork 這一份 [模板](https://github.com/jasonlu0525/nuxt3-live-question/tree/day21-vee-validate),完成以下條件 : - 將 `/pages/index.vue` 中的表單改成使用 VeeValidate 驗證。 - 所有欄位必需進行驗證,要求如下 : 1. 所有欄位都必需填寫。 2. 姓名欄位需要填寫至少 2 個字元。 3. 手機號碼欄位需要符合下方正規表達式的格式 : ```html /^(09)[0-9]{8}$/ ``` - 欄位驗證失敗時套用 Bootstrap 5 的 `is-invalid` 樣式。 - 使用 VeeValidate 的元件顯示驗證失敗的訊息。 - 透過 submit 事件處理表單提交。提交後使用 VeeValidate 的 resetForm 方法將表單重置。 - 模板已有安裝 VeeValidate 表單驗證所需套件,將 `@vee-validate/nuxt` 、驗證規則以及多國語系整合至 Nuxt3。以下是套件在使用上的細節 : - 表單驗證元件的名稱沒有限制,可以使用預設的元件 ( 如 `<Form>` ) 或是自訂元件名稱。 - 不需載入所有驗證規則,只需載入表單所需的規則即可。 - 多國語系的語言需使用繁體中文 ( zhTW )。 ## 回報流程 將答案上傳至 GitHub 並複製 GitHub repo 連結貼至底下回報就算完成了喔 ! 解答位置請參考下圖(需打開程式碼的部分觀看) ![](https://i.imgur.com/vftL5i0.png) <!-- 解答 : https://github.com/jasonlu0525/nuxt3-live-answer/tree/day21-vee-validate --> 回報區 --- | # | Discord | Github / 答案 | | --- | ----- | ----- | | 1 | Steven |[Github](https://github.com/y7516552/nuxt3-live-question/tree/day21)| | 2 | dragon |[Github](https://github.com/peterlife0617/2024-nuxt-training-homework01/tree/feature/day21)| | 3 | 眼睛 |[Github](https://github.com/Thrizzacode/nuxt3-live-question/tree/day21-vee-validate)| | 4 | LinaChen |[Github](https://github.com/Lina-SHU/nuxt3-live-question)| | 5 | Johnson |[Github](https://github.com/tttom3669/2024_hex_nuxt_daily/tree/day21-vee-validate)| | 6 | Rocky |[Github](https://github.com/WuRocky/Nuxt-Day21-VeeValidate)| | 7 | tanuki狸 |[Github](https://github.com/tanukili/Nuxt-2024-week01-2/tree/day21-vee-validate)| |8|hsin yu|[Github](https://github.com/dogwantfly/nuxt3-daily-task-live-question/tree/day21-vee-validate)| <!-- |---|---|[Github]()| -->