如果我們使用ui框架,通常都會有封裝好的form可以幫你驗證。

但是如果你沒有使用ui框架,驗證的效果就要自己寫囉!

validate.js的使用方法可以先參考官網,因為這邊只會介紹最基本的XD

首先你要先有一個vue3的專案。


安裝validate.js

$ yarn add validate.js

 

創建方法

把每次需要創建表單的方法包成composable

新增一隻檔案在composables/validate.js

import { reactive } from "vue";
import Validate from "validate.js";

export const useValidate = ({ defaultFormModel, constraints }) => {
  const formModel = reactive({ ...defaultFormModel });
  const errorText = reactive({ ...defaultFormModel });
  const handleValid = () => {
      // 等等我們要實作的驗證方法
  };

  return { formModel, errorText, handleValid };
};

我們會根據頁面的需要傳入defaultFormModel,是一個單純的物件,他的內容就是表單的欄位:

const defaultFormModel = {
    username: '',
    password: '',
    code: ''
};

  

formModelerrorText初始值都會是表單的欄位:

formModel是畫面中input要用v-model綁定的地方;errorText是當驗證出錯時,要顯示的錯誤訊息,也就是說errorText裡面的某個key的值是空的話,代表他驗證通過囉!

const formModel = reactive({ ...defaultFormModel });
const errorText = reactive({ ...defaultFormModel });

  

constraints就是要驗證的規則,假設我傳入的欄位需要驗證,就這樣輸入:

const constraints = {
    username: {
        presence: {
            allowEmpty: false,
            message: "請輸入電子郵件或手機號碼",
        },
    },
    password: {
        presence: {
            allowEmpty: false,
            message: "請輸入英數字混合密碼8~16位",
        },
        format: {
            pattern: "[a-z0-9]{8,16}$",
            flags: "i",
            message: "請輸入英數字混合密碼8~16位",
        },
    },
    code: {
        presence: {
            allowEmpty: false,
            message: "請輸入驗證碼",
        },
    }
}

  

handleValid是驗證方法,裡面就是直接呼叫validate.js的實例了:

import Validate from "validate.js";
//...
const handleValid = () => {
  const res = Validate(formModel, constraints,
    {
      fullMessages: false,
    }
  );
  res &&
    Object.keys(errorText).forEach((i) => {
      errorText[i] = res[i]?.[res[i].length - 1] || "";
    });
  res ||
    Object.keys(errorText).forEach((i) => {
      errorText[i] = "";
    });
  return res;
}

目前這個方法可以用在按下「送出」按鈕後,直接先驗證表單所有欄位。

  

但是我們會需要在單獨輸入某個欄位時,只驗證那個欄位。

這時候就可以用validate.single這個方法,或者自己改寫XD

我們在handleValid多一個參數傳入,傳入的我們希望他是要驗證的欄位名稱們,是一個陣列:

const attributes = ['username', 'password'];
const handleValid = (attributes) => {
    const validAttributes = attributes || Object.keys(errorText);
    const res = Validate(
      formModel,
      getObjectKeys(constraints, validAttributes),
      {
        fullMessages: false,
      }
    );
    res &&
      validAttributes.forEach((i) => {
        errorText[i] = res[i]?.[res[i].length - 1] || "";
      });
    res ||
      validAttributes.forEach((i) => {
        errorText[i] = "";
      });
    return res;
  };

其中getObjectKeys方法只是我一個轉換的util,它的作用是:

const constraints = {
  username: { 
    //...
  },
  password: {
    //...
  },
  code: {
    //...
  }
}
getObjectKeys(constraints, ['username', 'password'];
// output--> { username: {...}, password:{...} };

改寫後,當我們的input組件再輸入的時候也可以呼叫這個方法,只要再多傳入要驗證的欄位陣列就好。

  

頁面中使用

在畫面中我們會有input組件和@input方法:

<Input
    v-model="formModel.name"
    :errorText="errorText.name"
    placeholder="請輸入姓名"
    label="姓名"
    @handleOnInput="handleValid(['name'])"
/>
setup(){
  const { formModel, errorText, handleValid } = useValidate({
      defaultFormModel,
      constraints,
  });
  return {
      formModel,
      errorText,
      handleValid,
  };
}

<Input />這個組件是我自己封裝的,你也可以自己封裝外觀,我的原理是:errorText.name有東西的話就會顯示紅匡+錯誤訊息。訊息內容當然就是errorText.name的值囉!

@handleOnInput也是封裝的,他在底層是:

<input @input="handleOnInput" />
//...


const handleOnInput = () => {
    //...
    emit("handleOnInput");
};

送出表單,先驗證全部欄位:

<button @click="handleSubmit">送出</button>
//...


const handleSubmit = () => {
      const validRes = handleValid();
      if (validRes) return;
      // 寫入登入資訊
      // 回到首頁
};

  

完整程式碼

<template>
  <div class="auth">
    <h1>會員登入</h1>
      <Input
        v-model:inputText="formModel.username"
        :errorText="errorText.username"
        placeholder="電子郵件或手機號碼登入"
        label="電子郵件或手機"
        @handleOnInput="handleValid(['username'])"
      />
      <Input
        v-model:inputText="formModel.password"
        :errorText="errorText.password"
        placeholder="密碼"
        label="密碼"
        @handleOnInput="handleValid(['password'])"
      />
      <input
        type="checkbox"
        v-model="formModel.remember"
      />
      <button @click="handleSubmit">登入</button>
    </div>
  </div>
</template>
<script>
import Input from "@/components/Form/Input";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { username, password } from "@/utils/validateRules";
import { useValidate } from "@/composables/validate";
import { computed } from "@vue/runtime-core";
const defaultFormModel = {
  username: "",
  password: "",
  remember: true,
};
const constraints = {
  username,
  password,
};
export default {
  components: { Input },
  setup() {
    const store = useStore();
    const router = useRouter();
    const { formModel, errorText, handleValid } = useValidate({
      defaultFormModel,
      constraints,
    });
    const handleSubmit = () => {
      const validRes = handleValid();
      if (validRes) return;
      // store.commit("auth/setUser", { ...formModel });
      // router.push({ name: "Home" });
    };

    return {
      formModel,
      errorText,
      handleValid,
      handleSubmit,
    };
  },
};
</script>

import { reactive } from "vue";
import Validate from "validate.js";
import { getObjectKeys } from "@/utils";

export const useValidate = ({ defaultFormModel, constraints }) => {
  const formModel = reactive({
    ...defaultFormModel,
  });
  const errorText = reactive({ ...defaultFormModel });
  const handleValid = (attributes) => {
    const validAttributes = attributes || Object.keys(errorText);
    const res = Validate(
      formModel,
      getObjectKeys(constraints, validAttributes),
      {
        fullMessages: false,
      }
    );
    res &&
      validAttributes.forEach((i) => {
        errorText[i] = res[i]?.[res[i].length - 1] || "";
      });
    res ||
      validAttributes.forEach((i) => {
        errorText[i] = "";
      });
    return res;
  };
  return { formModel, errorText, handleValid };
};

export const username = {
  presence: {
    allowEmpty: false,
    message: "請輸入電子郵件或手機號碼",
  },
};
export const password = {
  presence: {
    allowEmpty: false,
    message: "請輸入英數字混合密碼8~16位",
  },
  format: {
    pattern: "[a-z0-9]{8,16}$",
    flags: "i",
    message: "請輸入英數字混合密碼8~16位",
  },
};
export const name = {
  presence: {
    allowEmpty: false,
    message: "請輸入姓名",
  },
};
export const phone = {
  presence: {
    allowEmpty: false,
    message: "請輸入電話號碼",
  },
  format: {
    pattern: "^09[0-9]{8}$",
    flags: "i",
    message: "請輸入09開頭的10位數字手機號碼",
  },
};
export const verifyCode = {
  presence: {
    allowEmpty: false,
    message: "請輸入認證碼",
  },
  numericality: { onlyInteger: true, message: "請輸入正確的認證碼" },
  length: {
    is: 6,
    message: "請輸入正確的認證碼",
  },
};
export const email = {
  presence: {
    allowEmpty: false,
    message: "請輸入Email",
  },
  email: {
    message: "請輸入合法的電子郵件",
  },
};

0
0 回復

發表評論

想要加入討論嗎?
請盡情發表您的想法!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。