---
# System prepended metadata

title: "\U0001F3C5 Day 21"
tags: [2024 Nuxt 直播班]

---

# 🏅 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]()|
 -->