Validação de formulário no Vue.js

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 exemplo

Mesmo 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 exemplo

Nã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 

 /* main.js */ 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> <!--             data-vv-as -          name -      --> <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> <!--           .      v-model- lazy     fields.passport_date  invalid  ,       touched ,      blur --> <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> <!--       ,    continues   errors.collect() --> <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: { // ,      isFormValid () { return Object.keys(this.fields).every(field => this.fields[field].valid); }, }, }; </script> 

Caixa de areia com um exemplo

Como 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      .      valid      --> <ValidationObserver v-slot="{ valid }"> <form @submit.prevent="doAction()"> <!-- ValidationProvider                 name - ,      --> <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> <!--         input,    blur  change  mode="lazy"  mode="eager" --> <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> <!--       ,     :bails="false" --> <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 exemplo

Outros 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 

 /* main.js */ 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()"> <!--      ,  $v.passportData.$invalid   ,     --> <div> <input type="text" v-model="passportData"> <span v-if="$v.passportData.$invalid">         1234 567890 </span> </div> <!--      blur  $touch()   $v.passportDate.$dirty  true.  $v.passportDate.$error   $v.passportDate.$invalid && $v.passportDate.$dirty --> <div> <input type="text" v-model="passportDate" @blur="$v.passportDate.$touch()"> <span v-if="$v.passportDate.$error">      .. </span> </div> <!-- ,       blur,     $v.passportDate.$model - ,     : - Vuelidate     passportDate - Vuelidate   $touch()   $v.passportDate  lazy ,      blur --> <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, }; }, //   ,  Vuelidate   computed- $v validations: { //        data passportData: { required, validFormat: val => /^\d{4} \d{6}$/.test(val), }, passportDate: { required, validDate: val => moment(val, 'DD.MM.YYYY', true).isValid(), }, name: { required, maxLength: maxLength(10), alpha: val => /^[-]*$/i.test(val), }, }, }; </script> 

Caixa de areia com um exemplo

Validadores 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!

Source: https://habr.com/ru/post/pt444900/


All Articles