Проверка формы - одна из самых сложных тем, с которой сталкиваются многие фронтенд-инженеры. Хотя реализовать это несложно, организовать логику валидации сложно.

С Vue у вас есть два основных варианта: проверка на основе шаблона с vee-validate или проверка на основе модели с vuelidate. Оба они - отличная библиотека. vee-validate очень прост и может быть быстрым решением. vuelidate обладает большей гибкостью.

С другой стороны, благодаря членам основной команды Vue выпускается Vue 3.0. Одно из серьезных изменений Vue 3.0 - это композиция api. Это позволяет нам писать более организованные и компонуемые коды.

Затем vuelidate также представит новый api с api композиции Vue 3.0. В этой статье объясняется, что он очень гибкий и позволяет разработчикам писать суперорганизованный код.

  • Разница в реализации между vuelidate, совместимым с Vue 2.x, и Vue 3.0
  • Как организовать логику валидации с помощью API композиции и vuelidate

Обзор валидаций Vue 2.x

Прежде чем представить следующее поколение vuelidate, позвольте мне показать вам, как это выглядит с Vue 2.x. Я подготовил простую форму входа, как показано ниже.

<template>
  <div>
    <input type="email" placeholder="Email" v-model="email" />
    <input type="password" placeholder="Password" v-model="password" />
    <button type="submit">Login</button>
  </div>
</template>

Чтобы реализовать логику проверки с помощью vuelidate, вам нужно сделать 3 вещи.

  1. Реализуйте логику проверки
  2. Контролируйте, когда проверять
  3. При необходимости показать ошибки

Реализуйте логику проверки

Логика проверки выглядит очень простой, как показано ниже. validations раздел используется для реализации того, как проверять все данные.

<script>
import { required, email, minLength } from "vuelidate/lib/validators";
export default {
  data() {
    return {
      email: "",
      password: "",
    }
  },
  validations: {
    email: { required, email },
    password: { required, minLength: minLength(8) }
  }
};
</script>

Если вы настроили, как указано выше, вы можете проверить статус валидации с помощью vue devtool. $v вычисляемое свойство показывает каждый статус проверки. Перед вводом любого значения $v.email.$invalid истинно, потому что проверка адреса электронной почты не выполняется. При вводе чего-либо $v.email.required становится ложным, а $v.email.email становится ложным. После ввода отформатированного электронного письма $v.email.email и $v.email.$invalid становятся истинными.

Контролируйте, когда проверять

Чтобы контролировать время проверки, вы должны знать свойства $error и $dirty. Свойство $error представляет собой логическую комбинацию $dirty и $invalid.

$invalid имеет статус проверки. Как вы проверили выше, $invalid истинно до того, как пользователь что-то сделает. Но только с $invalid, статус проверки не выполняется до того, как пользователь что-то сделает. Для решения этой проблемы существует свойство $dirty. $dirty проверка свойств, касается ли пользователь целевого поля ввода. Если пользователь что-то вводит, вы должны изменить $dirty на true.

Хорошо, достаточно подумать, когда проводить проверку. В этом примере эти 2 тайминга могут быть хорошими.

  • каждая проверка поля ввода должна запускаться на @blur
  • Перед отправкой формы. Лучше отключить кнопку входа в систему, если есть ошибка

Чтобы реализовать проверку времени размытия, вы можете добавить $touch() к @blur, как показано ниже. $touch функция изменит $dirty на true. Итак, свойства $dirty и $invalid готовы.

// To fire validation in each blur
  <input
    type="email"
    placeholder="Email"
    v-model="email"
    @blur="$v.email.$touch()"
  />
  ...
  <input
    type="password"
    placeholder="Password"
    v-model="password"
    @blur="$v.password.$touch()"
  />

Чтобы отключить кнопку отправки, вы можете передать $v.$invalid. Он проверяет $invalid всех ключей проверки. Тогда divtag должен быть тегом form и добавить вызов функции отправки. Внутри функции отправки сначала вызовите $touch(). Он запускает все проверки и проверяет наличие ошибок.

// To disable submit button disabled
<button :disabled="$v.$invalid" type="submit">Login</button>
// To validate before submission
<template>
  <form @submit.prevent="login" >
    ...
  </form>
</template>
<script>
...
  methods: {
    login() {
      this.$v.$touch();
      if (this.$v.$invalid) {
        // DO SOMETHING HERE
      }
    }
  }
...

Итак, теперь вы можете контролировать, когда проводить проверку.

При необходимости показать ошибки

Так что давайте, если нужно, покажем пользователям ошибки. Если вы хотите отображать ошибки, вы должны реализовать то, что показано ниже. Вам необходимо проверить, имеет ли целевое значение $error, и отображать сообщения об ошибках для каждой проверки.

<template>
  <form @submit.prevent="login">
    <input
      type="email"
      placeholder="Email"
      v-model="email"
      @blur="$v.email.$touch()"
    />
    <div v-if="$v.email.$error">
      <p v-if="!$v.email.email">Please enter a valid email</p>
      <p v-if="!$v.email.required">Please enter an email</p>
    </div>
    <input
      type="password"
      placeholder="Password"
      v-model="password"
      @blur="$v.password.$touch()"
    />
    <div v-if="$v.password.$error">
      <p v-if="!$v.password.minLength">
       Password must be more than {{ passwordMinLength }} characters
      </p>
      <p v-if="!$v.password.required">Please enter an password</p>
    </div>
    
    <button :disabled="$v.$invalid" type="submit">Login</button>
  </form>
</template>

При использовании vuelidate, совместимого с Vue 2.x, вам необходимо создавать свои собственные сообщения проверки.

Пока все проверки были выполнены. Позвольте мне показать вам, как выглядят все коды.

<template>
  <form @submit.prevent="login">
    <input
      type="email"
      placeholder="Email"
      v-model="email"
      @blur="$v.email.$touch()"
    />
    <div v-if="$v.email.$error">
      <p v-if="!$v.email.email">Please enter a valid email</p>
      <p v-if="!$v.email.required">Please enter an email</p>
    </div>
    <input
      type="password"
      placeholder="Password"
      v-model="password"
      @blur="$v.password.touch()"
    />
    <div v-if="$v.password.$error">
      <p v-if="!$v.password.minLength">
       Password must be more than {{ passwordMinLength }} characters
      </p>
      <p v-if="!$v.password.required">Please enter an password</p>
    </div>
    
    <button :disabled="$v.$invalid" type="submit">Login</button>
  </div>
</template>
<script>
import { required, email, minLength } from "vuelidate/lib/validators";
export default {
  data() {
    return {
      email: "",
      password: "",
    }
  },
  validations: {
    email: { required, email },
    password: { required, minLength: minLength(8) }
  },
  methods: {
    login() {
      this.$v.$touch();
      if (this.$v.$invalid) {
        // DO SOMETHING HERE
      }
    }
  }
};
</script>

Как вы понимаете, даже в этой сверхпростой форме входа в систему много кодов. Так что, если у вас больше поля и больше другой логики, все легко усложняется. Когда я впервые увидел эту реализацию, я подумал, что раздел validations и сообщение об ошибке в шаблоне можно извлечь или просто.

Проверка Vue 3.0 с помощью vuelidate

Api композиции Vue 3.0 позволяет нам очень легко составлять и организовывать логику. Если вы хотите узнать api композиции, посмотрите Vue mastery course.

Итак, давайте реализуем ту же форму входа с API композиции Vue 3.0 и vuelidate. Прежде всего, давайте создадим проект vue с api композиции. vue-cli - это самый простой способ. API композиции предоставляется с @vue/composition-api.

※ Если выпущен Vue 3.0, вы можете просто использовать vue-cli и установить Vue 3.0.

$ yarn global add @vue/cli  // If you don't have it
$ vue create vuelidate-with-vue-composition-api
$ cd vuelidate-with-vue-composition-api
$ yarn add @vue/composition-api

Измените src/components/HelloWorld.vue, как показано ниже.

// Form.vue
<template>
  <div>
    <input type="text" v-model="email">
    <input type="password" v-model="password">
    <button type="submit">Login</button>
  </div>
</template>
<script>
import { reactive, toRefs } from "@vue/composition-api";
export default {
  name: "Form",
  setup(){
    const state = reactive({
      email: "",
      password: ""
    });
    return {
      ...toRefs(state)
    };
  }
};
</script>
// STYLE CAN BE ANYTHING
<style scoped>
div,form {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
input, button {
  width: 300px;
  height: 50px;
  font-size: 1rem;
  margin: 20px auto 0px;
}
p {
  margin: 5px 60px 0 0;
}
</style>

И App.vue должен выглядеть так, как показано ниже.

// Replace HelloWorld component with Form component
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <Form />
  </div>
</template>
<script>
import Form from "./components/Form.vue";
export default {
  name: "App",
  components: {
    Form
  }
};
</script>

Итак, давайте добавим vuelidate. Фактически, начиная с версии, совместимой с Vue 3.0, vuelidate разделен на два пакета: @vuelidate/core и @vuelidate/validators. Логики проверки, такие как required и minLength, были разделены. И поскольку vuelidate все еще является бета-версией, для него требуется @vue/composition-api в качестве зависимостей, возможно, в ближайшем будущем он исчезнет.

$ yarn add @vuelidate/core @vuelidate/validators

И сначала вы должны настроить vuelidate и композицию api.

// main.js
import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';
import { VuelidatePlugin } from '@vuelidate/core';
import App from './App.vue';
Vue.config.productionTip = false;
Vue.use(VueCompositionApi);
Vue.use(VuelidatePlugin);
new Vue({
  render: h => h(App),
}).$mount("#app");

Все настройки были выполнены! Давайте реализуем проверку формы, как я сделал выше.

Реализуйте логику проверки

Новый vuelidate реализован с использованием API композиции Vue 3.0. Это выглядит так, как показано ниже. Во-первых, вы не можете использовать reactive сейчас. Это становится проблемой.

// Form.vue
<script>
import { ref } from "@vue/composition-api";
import useVuelidate from "@vuelidate/core";
import { required, email, minLength } from "@vuelidate/validators";
export default {
  name: "Form",
  setup(){
    const userEmail = ref("");
    const userPassword = ref("");
    // Validation Logics
    const rules = {
      userEmail: { required, email },
      userPassword: { required, minLength: minLength(8) }
    };
    const $v = useVuelidate(
      rules,
      { userEmail, userPassword }
    );
    return {
      userEmail,
      userPassword,
      $v
    };
  }
};
</script>

На самом деле, когда вы видите только логику проверки, это очень похоже на раздел Vue 2.x validations. С другой стороны, $v немного отличается от vuelidate, совместимого с Vue 2.0.

Как вы можете видеть выше, появилось новое свойство new$errors. Поскольку в нем хранятся сообщения об ошибках по умолчанию, вам не нужно писать собственное сообщение об ошибке.

Контролируйте, когда проверять

Давайте реализуем размытие полей ввода и проверку времени отправки.

К сожалению, поскольку родная функция $touch не выпущена, вы должны самостоятельно изменить $dirty на true. Он реализован в handleBlur и loginфункциях. Надеюсь, он будет выпущен в ближайшем будущем.

<template>
  <form @submit.prevent="login">
    <input
      type="text"
      v-model="userEmail"
      @blur="handleBlur('userEmail')"
    >
    ...
    <input
      type="password"
      v-model="userPassword"
      @blur="handleBlur('userPassword')"
    >
    <button :disabled="$v.$invalid" type="submit">Login</button>
  </form>
</template>
<script>
...
  setup(){
    
    ...
    
    const handleBlur = (key) =>{
      $v[key].$dirty = true;
    };
    const login = () => {
      $v.$dirty = true;
      if (!$v.$error) {
        // DO SOMETHING HERE
      }
    };
    return {
      userEmail,
      userPassword,
      $v,
      handleBlur
    };
  }
};
</script>

При необходимости показать ошибки

Как я уже упоминал выше, vuelidate, совместимый с Vue 3.0, имеет $errors, в котором хранятся сообщения об ошибках по умолчанию. Давайте использовать его с v-for.

<template>
  <form @submit.prevent="login">
    <input
      type="text"
      v-model="userEmail"
      @blur="handleBlur('userEmail')"
    >
    <div v-if="$v.userEmail.$error">
      <p v-for="$error in $v.userEmail.$errors" :key="$error.$property">
        {{ $error.$message }}
      </p>
    </div>
    <input
      type="password"
      v-model="userPassword"
      @blur="handleBlur('userPassword')"
    >
    <div v-if="$v.userPassword.$error">
      <p v-for="$error in $v.userPassword.$errors" :key="$error.$property">
        {{ $error.$message }}
      </p>
    </div>
    <button :disabled="$v.$invalid" type="submit">Login</button>
  </form>
</template>

Как видите, раздел сообщения об ошибке можно разделить на разные компоненты, как показано ниже.

// ErrorMessage.vue
<template>
  <div v-if="validationStatus.$error">
    <p v-for="$error in validationStatus.$errors" :key="$error.$property">
      {{ $error.$message }}
    </p>
  </div>
</template>
<script>
export default {
  name: "ErrorMessage",
  props: {
    validationStatus: {
      type: Object,
      required: true
    },
  }
};
</script>
// ADD STYLE IF YOU WANT

После создания компонента ErrorMessage ваш шаблон становится очень чистым. Раздел сообщения об ошибке в шаблоне извлечен 🎉

// Form.vue
<template>
  <form @submit.prevent="login">
    <input type="text" v-model="userEmail" @blur="handleBlur('userEmail')">
    <ErrorMessage :validationStatus="$v.userEmail" />
    <input type="password" v-model="userPassword" @blur="handleBlur('userPassword')">
    <ErrorMessage :validationStatus="$v.userPassword" />
  
    <button :disabled="$v.$invalid" type="submit">Login</button>
  </form>
</template>

Реализована вся логика валидации. Итак, позвольте мне показать вам весь код.

// Form.vue
<template>
  <form @submit.prevent="login">
    <input type="text" v-model="userEmail" @blur="handleBlur('userEmail')">
    <ErrorMessage :validationStatus="$v.userEmail" />
<input type="password" v-model="userPassword" @blur="handleBlur('userPassword')">
    <ErrorMessage :validationStatus="$v.userPassword" />
  
    <button :disabled="$v.$invalid" type="submit">Login</button>
  </form>
</template>
<script>
import { ref } from "@vue/composition-api";
import useVuelidate from "@vuelidate/core";
import { required, email, minLength } from "@vuelidate/validators";
import ErrorMessage from "./ErrorMessage.vue";
export default {
  name: "Form",
  components: {
    ErrorMessage,
  },
  setup(){
    const userEmail = ref("");
    const userPassword = ref("");
    const rules = {
      userEmail: { required, email },
      userPassword: { required, minLength: minLength(8) }
    };
    const $v = useVuelidate(
      rules,
      { userEmail, userPassword }
    );
    const handleBlur = (key) =>{
      $v[key].$dirty = true;
    };
    const login = () => {
      $v.$dirty = true;
      if (!$v.$error) {
        // DO SOMETHING
      }
    };
    return {
      userEmail,
      userPassword,
      $v,
      handleBlur,
      login
    };
  }
};
</script>

С API композиции Vue 3.0 у вас уже есть две вещи.

  1. Поскольку вам не нужно писать собственные сообщения об ошибках, создать ErrorMessage компонент очень просто.
  2. Хотя логики проверки разделены в validations, methods с Vue 2.x, все связанные с проверкой логики организованы в одном месте с Vue 3.0.

Но композиция api может сделать ваш код намного чище. Позвольте мне показать вам, как это сделать!

Составьте логику проверки

Сначала создайте функцию useLoginForm в Form.vue и скопируйте и вставьте всю логику в setup, как показано ниже.

// Form.vue
<script>
import { ref } from "@vue/composition-api";
...
export default {
  name: "Form",
  components: { ErrorMessage },
  setup(){    
    const { userEmail, userPassword, $v, handleBlur, login } = useLoginForm();
    return {
      userEmail,
      userPassword,
      $v,
      handleBlur,
      login
    };
  }
};
const useLoginForm = () =>{
  const userEmail = ref("");
  const userPassword = ref("");
  const rules = {
    userEmail: { required, email },
    userPassword: { required, minLength: minLength(8) }
  };
  const $v = useVuelidate(
    rules,
    { userEmail, userPassword }
  );
  const handleBlur = (key) =>{
    $v[key].$dirty = true;
  };
  const login = () => {
    $v.$dirty = true;
    if (!$v.$error) {
      // DO SOMETHING
    }
  };
  return {
    userEmail,
    userPassword,
    $v,
    handleBlur,
    login
  };
};
</script>

Поскольку useLoginForm функция - это просто функция, ее можно выделить в другой файл, например ,src/composables/useLoginForm.js.

// Form.vue
<script>
import { useLoginForm } from "../composables/useLoginForm";
import ErrorMessage from "./ErrorMessage.vue";
export default {
  name: "Form",
  components: { ErrorMessage },
  setup(){    
    const { userEmail, userPassword, $v, handleBlur, login } = useLoginForm();
return {
      userEmail,
      userPassword,
      $v,
      handleBlur,
      login
    };
  }
};
</script>

// src/composables/useLoginForm.js
import { ref } from "@vue/composition-api";
import useVuelidate from "@vuelidate/core";
import { required, email, minLength } from "@vuelidate/validators";
export const useLoginForm = () =>{
  const userEmail = ref("");
  const userPassword = ref("");
  const rules = {
    userEmail: { required, email },
    userPassword: { required, minLength: minLength(8) }
  };
  const $v = useVuelidate(
    rules,
    { userEmail, userPassword }
  );
  const handleBlur = (key) =>{
    $v[key].$dirty = true;
  };
  const login = () => {
    $v.$dirty = true;
    if (!$v.$error) {
      // DO SOMETHING
    }
  };
  return {
    userEmail,
    userPassword,
    $v,
    handleBlur,
    login
  };
};

Теперь Form.vue супер чисто, но понятно! Даже если в Form.vue добавлены другие логики, такие как выборка, вы можете составить его таким же образом и сохранить Form.vue очень чистым.

Заворачивать

Пока в этой статье объясняется, что

  • Разница в реализации между vuelidate, совместимым с Vue 2.x, и Vue 3.0
  • Как организовать логику валидации с помощью API композиции и vuelidate

Если есть вопросы, пишите комментарии! Я очень жду релиза Vue 3.0! Спасибо.

использованная литература