Olá Habr!
A validação de formulário é uma das tarefas mais importantes do site. Temos que validar os dados para presença, para corresponder ao padrão, criar validações assíncronas, aplicar validações somente após remover o foco do campo ou antes de enviar o formulário ... Às vezes isso se torna uma verdadeira dor de cabeça para o desenvolvedor.
O Vue.js contém muitas abordagens de validação interessantes e incomuns que ajudarão a resolver seus problemas. Visão geral sob o corte!
TL; DR
Use
Vuelidate .
Erros comuns
Validações HTML5
O HTML5 deu aos desenvolvedores a capacidade de validar formulários usando
novos atributos de campo e a API de validação . Podemos usá-los diretamente em nossos modelos vue.
Aqui, por exemplo, há um formulário de registro simples, composto por três campos: campos para email, para senha e repetição de senha. Para os dois primeiros, usamos validações usando atributos, para o terceiro - usando atributos e a API de validação:
<template> <form @submit.prevent="someAction()"> <input v-model="email" type="email" required> <input v-model="password" type="password" required> <input v-model="repeatedPassword" type="password" required ref="repeatedPasswordEl"> <button type="submit"> </button> </form> </template> <script> export default { data() { return { email: null, password: null, repeatedPassword: null, }; }, watch: { repeatedPassword: 'checkPasswordsEquality', password: 'checkPasswordsEquality', }, methods: { checkPasswordsEquality() { const { password, repeatedPassword } = this; const { repeatedPasswordEl } = this.$refs; if (password !== repeatedPassword) { repeatedPasswordEl.setCustomValidity( ' ', ); } else { repeatedPasswordEl.setCustomValidity(''); } }, }, }; </script>
Caixa de areia com um exemploMesmo em um exemplo tão simples, você pode ver muitos problemas:
- Os navegadores mostram apenas um erro de cada vez. O usuário terá que tentar enviar o formulário várias vezes para ver todos os seus erros.
- Os erros são exibidos ao usuário somente depois de tentar enviar o formulário. Para alterar esse comportamento, você precisará escrever outra tonelada de código: chame a função reportValidity () em cada elemento no evento de desfoque.
- Para estilizar campos de entrada, existem apenas pseudo-classes: válidas e: inválidas, não há como capturar um estado quando os dados no campo de entrada estão incorretos, mas o usuário ainda não interagiu com eles.
- Cada navegador exibe erros de validação à sua maneira; eles podem parecer feios no seu design.
- A API de validação no Vue.js é inconveniente de usar: você precisa salvar os elementos em $ refs.
Se você tiver apenas um formulário no projeto e ele for usado apenas por você, a validação HTML5 é uma excelente opção. Em todos os outros casos, tente usar outras abordagens.
Validações da biblioteca
Outro problema comum que encontro nos projetos é a validação de biblioteca.
<template> <form @submit.prevent="someAction()"> <input v-model="email" type="email" @blur="isEmailTouched = true" :class="{ error: isEmailError }" > <div v-if="isEmailError"> </div> <button :disabled="!isEmailValid" type="submit"> </button> </form> </template> <script> const emailCheckRegex = /^... RegExp Email...$/; export default { data() { return { email: null, isEmailTouched: false, }; }, computed: { isEmailValid() { return emailCheckRegex.test(this.email); }, isEmailError() { return !this.isEmailValid && this.isEmailTouched; }, }, }; </script>
Caixa de areia com um exemploNão há problema com essa abordagem. Exceto que, em formulários com mais de um campo, nasce muito código repetido e cada desenvolvedor escreve validações à sua maneira.
A melhor saída para todos os problemas é usar as bibliotecas que a comunidade nos oferece. Atualmente, existem duas soluções populares de validação de formulários:
Cada um tem sua própria abordagem única, que consideraremos em detalhes abaixo.
validar vee
vee-validate - uma biblioteca de validações que apareceu durante a primeira versão do Vue.js, possui uma grande comunidade e é usada em um grande número de projetos.
A idade afeta o peso - 31 KB (reduzido + GZIP), uma vez e meia mais que o próprio Vue.js! Isso ocorre porque a biblioteca contém imediatamente várias coisas:
- 35 validadores embutidos
- Tradução para inglês de erros
- Diretivas de validação
- Componentes de validação
A biblioteca suporta 41 idiomas para erros. Para instalar e usá-lo com a localização desejada, você precisa executar apenas algumas etapas:
npm i vee-validate
import Vue from 'vue'; import VeeValidate, { Validator } from 'vee-validate'; import ru from 'vee-validate/dist/locale/ru'; Validator.localize('ru', ru); Vue.use(VeeValidate, { locale: 'ru', });
O Vee-validate possui duas abordagens para validação: usando a diretiva e usando componentes.
Validação com diretiva v-validate
A abordagem usando a diretiva é muito simples: você coloca a diretiva v-validate no campo de entrada, para o qual passa a lista de validadores. O status de validação e as linhas de erro podem ser obtidas nos campos e erros dos campos dentro do componente.
Para ver seu uso como um exemplo, vamos criar um formulário simples que:
- Contém o campo "Número de série e passaporte"
- Contém o campo "Data de emissão do passaporte"
- Contém o campo "Nome"
- Adiciona o atributo desativado ao botão de envio do formulário se os dados estiverem incorretos
<template> <form @submit.prevent="someAction()"> <div> <input type="text" v-model="passportData" v-validate="{ required: true, regex: /^\d{4} \d{6}$/ }" data-vv-as=" " name="passport_data" > <span v-if="errors.has('passport_data')"> {{ errors.first('passport_data') }} </span> </div> <div> <input type="text" v-model.lazy="passportDate" v-validate="{ required: true, date_format: 'dd.MM.yyyy' }" data-vv-as=" " name="passport_date" > <span v-if="fields.passport_date && fields.passport_date.touched && fields.passport_date.invalid" > {{ errors.first('passport_date') }} </span> </div> <div> <input type="text" v-model="name" v-validate.continues="{ required: true, alpha: true, max: 10 }" data-vv-as="" name="name" > <span v-for="error in errors.collect('name')" :key="error" > {{ error }} </span> </div> <button type="submit" :disabled="!isFormValid" > </button> </form> </template> <script> export default { data() { return { passportData: null, name: null, passportDate: null, }; }, computed: { </script>
Caixa de areia com um exemploComo você pode ver, ao usar os sinalizadores dentro de fields.passport_date, tive que verificar a presença do campo passport_date - durante a primeira renderização, vee-validate não tem informações sobre seus campos e, portanto, não há objeto com sinalizadores. Para se livrar dessa verificação, use o auxiliar especial
mapFields .
Validações de componentes
O novo método de validação que apareceu no final do ano passado é o uso de componentes. Os próprios autores recomendam o uso dessa abordagem, e ela vai bem com a nova sintaxe de slot de Vue.js@2.6.
A biblioteca fornece dois componentes:
- ValidationProvider - um componente no qual você descreve as validações e no qual quebra o campo de entrada.
- ValidationObserver - um componente que combina os sinalizadores de validação de vários ValidationProviders.
Aqui está um formulário de um exemplo anterior, mas escrito usando componentes:
<template> <ValidationObserver v-slot="{ valid }"> <form @submit.prevent="doAction()"> <ValidationProvider name=" " :rules="{ required: true, regex: /^\d{4} \d{6}$/ }" v-slot="{ errors }" > <input type="text" v-model="passportData"> <span v-if="errors[0]"> {{ errors[0] }} </span> </ValidationProvider> <ValidationProvider name=" " :rules="{ required: true, date_format: 'dd.MM.yyyy' }" v-slot="{ errors }" mode="lazy" > <input type="text" v-model="passportDate"> <span v-if="errors[0]"> {{ errors[0] }} </span> </ValidationProvider> <ValidationProvider name="" :rules="{ required: true, alpha: true, max: 10 }" v-slot="{ errors }" :bails="false" > <input type="text" v-model="name"> <span v-for="error in errors" :key="error" > {{ error }} </span> </ValidationProvider> <button type="submit" :disabled="!valid"> </button> </form> </ValidationObserver> </template> <script> import { ValidationProvider, ValidationObserver } from 'vee-validate'; export default { components: { ValidationProvider, ValidationObserver, }, data() { return { passportData: null, passportDate: null, name: null, }; }, }; </script>
Caixa de areia com um exemploOutros recursos da biblioteca
Os problemas
O primeiro e maior problema é, obviamente, seu tamanho. A biblioteca não suporta trepidação de árvore e adição seletiva de validadores. Quer você goste ou não, sua gangue sempre terá 2 componentes e uma diretiva de validação, uma tradução para o inglês e 35 validadores.
O segundo problema é devido à abordagem de assinatura baseada em eventos; podem surgir problemas ao integrar-se com outras bibliotecas que também alteram o comportamento dos campos de entrada (mascaradores, etc.).
O terceiro problema é mais subjetivo - as traduções de erros são gerais e feias, não refletem a essência real.
Pegue o formulário de exemplos anteriores. Se você digitar o número e a data incorretos do passaporte, receberá os seguintes erros:
. dd.MM.yyyy.
Gostaria de substituí-los por algo mais legível:
1234 567890 ..
Vuelidate
A biblioteca Vuelidate apareceu em resposta a problemas com as abordagens que a biblioteca vee-validate contém. O Vuelidate não possui manipuladores de eventos para campos nem traduções para erros de validação.
Requer apenas uma coisa: descrever validações no objeto validações. Então ela própria criará um campo calculado $ v com sinalizadores de validação de campo e funções para alterar esses sinalizadores.
Graças à sua abordagem simples de validação, o Vuelidate pesa apenas 3,4 KB (minificado + GZIP), quase 10 vezes menos que a validação por v.
É instalado com a mesma facilidade:
npm i vuelidate
import Vue from 'vue' import Vuelidate from 'vuelidate' Vue.use(Vuelidate)
Reescreva o formulário do exemplo anterior usando o Vuelidate:
<template> <form @submit.prevent="someAction()"> <div> <input type="text" v-model="passportData"> <span v-if="$v.passportData.$invalid"> 1234 567890 </span> </div> <div> <input type="text" v-model="passportDate" @blur="$v.passportDate.$touch()"> <span v-if="$v.passportDate.$error"> .. </span> </div> <div> <input type="text" v-model.lazy="$v.passportDate.$model"> <span v-if="$v.passportDate.$error"> .. </span> </div> <div> <input type="text" v-model="name" @blur="$v.name.$touch()"> <span v-if="$v.name.$error"> <template v-if="!$v.name.maxLength"> {{ $v.name.$params.maxLength.max }} </template> <template v-else-if="!$v.name.alpha"> </template> <template v-else> </template> </span> </div> <button type="submit" :disabled="$v.$invalid"> </button> </form> </template> <script> import { required, maxLength } from 'vuelidate/lib/validators'; import moment from 'moment'; export default { data() { return { passportData: null, name: null, passportDate: null, }; }, </script>
Caixa de areia com um exemploValidadores personalizados podem ser descritos rápida e facilmente usando funções. Se você deseja que os parâmetros do validador caiam no objeto $ params, use o auxiliar especial
withParams .
Como você pode ver, os campos com vários erros ocupam muito espaço e parecem monstruosos - isso é resolvido criando um componente separado para exibir validações.
Outros recursos da biblioteca
Os problemas
Dentre os problemas, pode-se destacar o número comparativamente reduzido de validadores prontos para uso; geralmente você precisa escrever suas próprias funções para validação.
Conclusão
Usei a biblioteca Vuelidate em muitos projetos e nunca encontrei problemas insolúveis. Para mim, ela continua sendo a melhor escolha. Se você se preocupa com o tamanho do seu código e prefere uma abordagem de modelo à descrição das validações - aceite, documentação simples e API rica permitirão validar formas de qualquer complexidade.
Se você estiver executando o painel de administração para uso interno, não deseje gastar uma única gota de tempo nas linhas de erro - escolha validar vee. Ajudará a escrever muitas validações rapidamente e sem problemas.
Obrigado pela atenção!