Validação de formas complexas de React. Parte 1

Primeiro você precisa instalar o componente react-validation-boo , presumo que você esteja familiarizado com o react e saiba como configurá-lo.

npm install reat-validation-boo

Para não falar muito, darei imediatamente um pequeno exemplo de código.

import React, {Component} from 'react'; import {connect, Form, Input, logger} from 'react-validation-boo'; class MyForm extends Component { sendForm = (event) => { event.preventDefault(); if(this.props.vBoo.isValid()) { console.log('       ', this.props.vBoo.getValues()); } else { console.log('   ', this.props.vBoo.getErrors()); } }; getError = (name) => { return this.props.vBoo.hasError(name) ? <div className="error">{this.props.vBoo.getError(name)}</div> : ''; }; render() { return <Form connect={this.props.vBoo.connect}> <div> <Input type="text" name="name" /> {this.getError('name')} </div> <button onClick={this.sendForm}> {this.props.vBoo.isValid() ? ' ': ' !!!'} </button> </Form> } } export default connect({ rules: () => ( [ ['name', 'required'], ] ), middleware: logger })(MyForm); 


Vamos analisar esse código.

Vamos começar com a função connect , passamos nossas regras de validação e outros parâmetros adicionais para ela. Ao chamar esse método, obtemos uma nova função na qual passamos nosso componente ( MyForm ) para que ele receba os métodos necessários para trabalhar com a validação de formulário em props .

Na função render do nosso componente, retornamos o componente Form que conectamos às regras de validação connect = {this.props.connect} . Esse é um design necessário para o Form saber como validar componentes aninhados.
<Input type = "text" name = "name" /> o campo de entrada que verificaremos, passamos as regras de verificação para conectar - se à propriedade de regras . No nosso caso, esse nome não deve estar vazio ( obrigatório ).

Também passamos o middleware: logger para conectar - se para ver como a validação funciona no console.

Nos props do nosso componente, temos um conjunto de funções:

  1. vBoo.isValid () - retorna true se todos os componentes de entrada foram validados
  2. vBoo.hasError (name) - retorna true se o componente com a propriedade name não for válido
  3. vBoo.getError (name) - para um componente com a propriedade name , retorna o texto do erro

Agora vamos complicar gradualmente, primeiro passaremos o idioma para a conexão , para que possamos alterar as regras de validação dependendo do idioma e também adicionar campos e regras de validação adicionais.

 import React, {Component} from 'react'; import {connect, Form, Input, InputCheckbox} from 'react-validation-boo'; class MyForm extends Component { sendForm = (event) => { event.preventDefault(); if(this.props.vBoo.isValid()) { console.log('       ', this.props.vBoo.getValues()); } else { console.log('   ', this.props.vBoo.getErrors()); } }; getError = (name) => { return this.props.vBoo.hasError(name) ? <div className="error">{this.props.vBoo.getError(name)}</div> : ''; }; render() { return <Form connect={this.props.vBoo.connect}> <div> <label>{this.props.vBoo.getLabel('name')}:</label> <Input type="text" name="name" /> {this.getError('name')} </div> <div> <label>{this.props.vBoo.getLabel('email')}:</label> <Input type="text" name="email" value="default@mail.ru" /> {this.getError('email')} </div> <div> <label>{this.props.vBoo.getLabel('remember')}:</label> <InputCheckbox name="remember" value="yes" /> {this.getError('remember')} </div> <button onClick={this.sendForm}> {this.props.vBoo.isValid() ? ' ': ' !!!'} </button> </Form> } } export default connect({ rules: (lang) => { let rules = [ [ ['name', 'email'], 'required', { error: '%name%    ' } ], ['email', 'email'] ]; rules.push(['remember', lang === 'ru' ? 'required': 'valid']); return rules; }, labels: (lang) => ({ name: '', email: ' ', remember: '' }), lang: 'ru' })(MyForm); 

Neste exemplo, a caixa de seleção lembrar em russo deve ser instalada necessária , mas em outros é sempre válida.

Também passamos os rótulos da função (lang) para conectar , que retornam o nome dos campos em um formato legível.

Nos props do seu componente, há uma função getLabel (name) que retorna o valor passado para a função labels , ou, se não houver esse valor, retorna name .

Componentes principais do VBoo


Form , Input , InputRadio , InputCheckbox , Select , Textarea .

 import React, {Component} from 'react'; import {connect, Form, Input, Select, InputRadio, InputCheckbox, Textarea} from 'react-validation-boo'; class MyForm extends Component { sendForm = (event) => { event.preventDefault(); if(this.props.vBoo.isValid()) { console.log('       ', this.props.vBoo.getValues()); } else { console.log('   ', this.props.vBoo.getErrors()); } }; getError = (name) => { return this.props.vBoo.hasError(name) ? <div className="error">{this.props.vBoo.getError(name)}</div> : ''; }; render() { return <Form connect={this.props.vBoo.connect}> <div> <label>{this.props.vBoo.getLabel('name')}:</label> <Input type="text" name="name" /> {this.getError('name')} </div> <div> <label>{this.props.vBoo.getLabel('email')}:</label> <Input type="text" name="email" value="default@mail.ru" /> {this.getError('email')} </div> <div> <label>{this.props.vBoo.getLabel('gender')}:</label> <Select name="gender"> <option disabled> </option> <option value="1"></option> <option value="2"></option> </Select> {this.getError('gender')} </div> <div> <div>{this.props.vBoo.getLabel('familyStatus')}:</div> <div> <InputRadio name="familyStatus" value="1" checked /> <label></label> </div> <div> <InputRadio name="familyStatus" value="2" /> <label></label> </div> <div> <InputRadio name="familyStatus" value="3" /> <label></label> </div> {this.getError('familyStatus')} </div> <div> <label>{this.props.vBoo.getLabel('comment')}:</label> <Textarea name="comment"></Textarea> {this.getError('comment')} </div> <div> <label>{this.props.vBoo.getLabel('remember')}:</label> <InputCheckbox name="remember" value="yes" /> {this.getError('remember')} </div> <button onClick={this.sendForm}> {this.props.vBoo.isValid() ? ' ': ' !!!'} </button> </Form> } } export default connect({ rules: () => ([ [ ['name', 'email'], 'required', { error: '%name%    ' } ], ['email', 'email'], [['gender', 'familyStatus', 'comment', 'remember'], 'valid'] ]), labels: () => ({ name: '', email: ' ', gender: '', familyStatus: ' ', comment: '', remember: '' }), lang: 'ru' })(MyForm); 

Regras de validação


Vamos ver como escrever nossas próprias regras de validação.
Para escrever uma regra, você deve criar uma classe que será herdada da classe validadora .

 import {validator} from 'react-validation-boo'; class myValidator extends validator { /** * name -  ,   label    * value -    * params -     3-    (rules) */ validate(name, value, params) { let lang = this.getLang(); let pattern = /^\d+$/; if(!pattern.test(value)) { let error = params.error || '   %name%   %value%'; error = error.replace('%name%', name); error = error.replace('%value%', value); this.addError(error); } } } export default myValidator; 

Agora conecte nosso validador ao formulário.
 import myValidator from 'path/myValidator'; // ... export default connect({ rules: () => ([ [ 'name', 'required', { error: '%name%    ' } ], [ 'name', 'myValidator', { error: '   params.error' } ] ]), labels: () => ({ name: '' }), validators: { myValidator }, lang: 'ru' })(MyForm); 

Para não registrar todas as suas regras de validação de cada vez, crie um arquivo separado onde elas serão registradas e conecte seus validadores: `import 'file-validation`` . E se houver alguma regra especial para este formulário, os validadores: Object.assign ({}, `import 'file-validation``, {...})

Cenários


Considere os casos em que precisamos alterar as regras de validação, dependendo das ações executadas no formulário.

Por padrão, temos um script chamado default , nas regras que podemos especificar em qual cenário realizar essa validação.

Se nenhum script for especificado, a validação será executada para todos os cenários.

 rules = () => ([ [ 'name', 'required', { error: '%name%    ' } ], [ 'name', 'myValidator', { scenario: ['default', 'scenario1'] } ], [ 'email', 'email', { scenario: 'scenario1' } ] ]) 

A função é passada através da propriedade props do nosso componente:

  1. vBoo.setScenario (cenário) - define o script do cenário; ele pode ser uma sequência ou uma matriz, se houver vários scripts ativos ao mesmo tempo
  2. vBoo.getScenario () - retorna o script atual ou a matriz de scripts
  3. vBoo.hasScenario (name) - se este script está instalado agora, name string

Vamos adicionar um objeto de cenários em nosso formulário, no qual armazenaremos todos os scripts possíveis; verdadeiro , o script está ativo; falso , não.

Além das funções addScenaries e deleteScenaries que adicionarão e removerão scripts.

Se selecionarmos "coabitação" ou "casamento", adicionaremos um campo de comentário e, naturalmente, esse campo deverá ser validado apenas nesse caso, o cenário casado com o cenário .

Se tivermos a caixa de seleção "Avançado" definida, adicionaremos campos adicionais que serão necessários, o script ' adição de cenário '.

 import React, {Component} from 'react'; import {connect, Form, Input, Select, InputRadio, InputCheckbox, Textarea} from 'react-validation-boo'; class MyForm extends Component { constructor() { super(); this.scenaries = { 'scenario-married': false, 'scenario-addition': false } } changeScenaries(addScenaries = [], deleteScenaries = []) { addScenaries.forEach(item => this.scenaries[item] = true); deleteScenaries.forEach(item => this.scenaries[item] = false); let scenario = Object.keys(this.scenaries) .reduce((result, item) => this.scenaries[item]? result.concat(item): result, []); this.props.vBoo.setScenario(scenario); } addScenaries = (m = []) => this.changeScenaries(m, []); deleteScenaries = (m = []) => this.changeScenaries([], m); sendForm = (event) => { event.preventDefault(); if(this.props.vBoo.isValid()) { console.log('       ', this.props.vBoo.getValues()); } else { console.log('   ', this.props.vBoo.getErrors()); } }; getError = (name) => { return this.props.vBoo.hasError(name) ? <div className="error">{this.props.vBoo.getError(name)}</div> : ''; }; changeFamilyStatus = (event) => { let val = event.target.value; if(val !== '1') { this.addScenaries(['scenario-married']) } else { this.deleteScenaries(['scenario-married']); } }; changeAddition = (event) => { let check = event.target.checked; if(check) { this.addScenaries(['scenario-addition']) } else { this.deleteScenaries(['scenario-addition']); } }; getCommentContent() { if(this.props.vBoo.hasScenario('scenario-married')) { return ( <div key="comment-content"> <label>{this.props.vBoo.getLabel('comment')}:</label> <Textarea name="comment"></Textarea> {this.getError('comment')} </div> ); } return ''; } getAdditionContent() { if(this.props.vBoo.hasScenario('scenario-addition')) { return ( <div key="addition-content"> <label>{this.props.vBoo.getLabel('place')}:</label> <Input type="text" name="place" /> {this.getError('place')} </div> ); } return ''; } render() { return <Form connect={this.props.vBoo.connect}> <div> <label>{this.props.vBoo.getLabel('name')}:</label> <Input type="text" name="name" /> {this.getError('name')} </div> <div> <label>{this.props.vBoo.getLabel('email')}:</label> <Input type="text" name="email" value="default@mail.ru" /> {this.getError('email')} </div> <div> <label>{this.props.vBoo.getLabel('gender')}:</label> <Select name="gender"> <option disabled> </option> <option value="1"></option> <option value="2"></option> </Select> {this.getError('gender')} </div> <div> <div>{this.props.vBoo.getLabel('familyStatus')}:</div> <div> <InputRadio name="familyStatus" value="1" checked onChange={this.changeFamilyStatus} /> <label></label> </div> <div> <InputRadio name="familyStatus" value="2" onChange={this.changeFamilyStatus} /> <label></label> </div> <div> <InputRadio name="familyStatus" value="3" onChange={this.changeFamilyStatus} /> <label></label> </div> {this.getError('familyStatus')} </div> {this.getCommentContent()} <div> <label>{this.props.vBoo.getLabel('addition')}:</label> <InputCheckbox name="addition" value="yes" onChange={this.changeAddition} /> {this.getError('addition')} </div> {this.getAdditionContent()} <button onClick={this.sendForm}> {this.props.vBoo.isValid() ? ' ': ' !!!'} </button> </Form> } } export default connect({ rules: () => ([ [ ['name', 'gender', 'familyStatus', 'email'], 'required', { error: '%name%    ' } ], ['email', 'email'], [ 'comment', 'required', { scenario: 'scenario-married' } ], ['addition', 'valid'], [ 'place', 'required', { scenario: 'scenario-addition' } ], ]), labels: () => ({ name: '', email: ' ', gender: '', familyStatus: ' ', comment: '', addition: '', place: '' }), lang: 'ru' })(MyForm); 

Para não tornar o artigo muito grande, continuarei no próximo, onde escreverei como criar meus próprios componentes (por exemplo, um calendário ou inputSearch) e validá-los, como se relacionar com redux e muito mais.

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


All Articles