Validation de formes complexes de React. Partie 1

Vous devez d'abord installer le composant react-validation-boo , je suppose que vous êtes familier avec react et savez comment le configurer.

npm installer react-validation-boo

Afin de ne pas trop parler, je vais immédiatement donner un petit exemple de code.

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); 


Analysons ce code.

Commençons par la fonction connect , nous lui passons nos règles de validation et d'autres paramètres supplémentaires. En appelant cette méthode, nous obtenons une nouvelle fonction dans laquelle nous transmettons notre composant ( MyForm ) afin qu'il reçoive les méthodes nécessaires pour travailler avec la validation de formulaire dans les accessoires .

Dans la fonction de rendu de notre composant, nous renvoyons le composant Form que nous connectons aux règles de validation connect = {this.props.connect} . Il s'agit d'une conception nécessaire pour que Form sache comment valider les composants imbriqués.
<Input type = "text" name = "name" /> le champ de saisie que nous allons vérifier, nous avons passé les règles de vérification pour se connecter dans la propriété rules . Dans notre cas, ce nom ne doit pas être vide ( obligatoire ).

Nous avons également passé le middleware: logger pour se connecter afin de voir comment fonctionne la validation dans la console.

Dans les accessoires de notre composant, nous avons obtenu un ensemble de fonctions:

  1. vBoo.isValid () - retourne vrai si tous les composants d'entrée ont été validés
  2. vBoo.hasError (name) - renvoie true si le composant avec la propriété name n'est pas valide
  3. vBoo.getError (nom) - pour un composant avec la propriété name , retourne le texte d'erreur

Maintenant, nous allons le compliquer progressivement, nous allons d'abord passer la langue pour se connecter , afin que nous puissions changer les règles de validation en fonction de la langue, et également ajouter des champs supplémentaires et des règles de validation.

 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); 

Dans cet exemple, la case à cocher se souvenir en russe doit être installée obligatoire , mais dans d'autres elle est toujours valide.

Nous avons également passé les étiquettes de fonction (lang) pour se connecter , qui renvoie le nom des champs sous une forme lisible.

Dans les accessoires de votre composant, il y a une fonction getLabel (nom) qui retourne la valeur passée à la fonction labels , ou s'il n'y a pas une telle valeur, elle retourne le nom .

Composants principaux de 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); 

Règles de validation


Voyons comment écrire nos propres règles de validation.
Pour écrire une règle, vous devez créer une classe qui sera héritée de la classe de validateur .

 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; 

Connectez maintenant notre validateur au formulaire.
 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); 

Afin de ne pas enregistrer toutes vos règles de validation à chaque fois, créez un fichier séparé où elles seront enregistrées et connectez ses valideurs: `` import '' file-validation`` . Et s'il existe des règles spéciales pour ce formulaire, alors les validateurs: Object.assign ({}, `import 'file-validation``, {...})

Scénarios


Considérez les cas où nous devons modifier les règles de validation en fonction des actions effectuées sur le formulaire.

Par défaut, nous avons un script appelé default , dans les règles nous pouvons spécifier sous quel scénario effectuer cette validation.

Si aucun script n'est spécifié, la validation sera effectuée pour tous les scénarios.

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

La fonction est passée à travers la propriété props de notre composant:

  1. vBoo.setScenario (scenario) - définit le script de scénario , il peut s'agir d'une chaîne ou d'un tableau, si nous avons plusieurs scripts actifs à la fois
  2. vBoo.getScenario () - retourne le script ou le tableau de scripts en cours
  3. vBoo.hasScenario (nom) - si ce script est installé maintenant, chaîne de nom

Ajoutons un objet scenaries dans notre formulaire, dans lequel nous allons stocker tous les scripts possibles, true le script est actif, false ne l' est pas.

Ainsi que les fonctions addScenaries et deleteScenaries qui ajouteront et supprimeront des scripts.

Si nous avons sélectionné «cohabitation» ou «mariage», nous ajoutons un champ de commentaire et naturellement ce champ ne doit être validé que dans ce cas, le scénario marié .

Si nous avons la case à cocher "Avancé", alors nous ajoutons des champs supplémentaires qui seront requis, le script ' scenario-addition '.

 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); 

Afin de ne pas rendre l'article très volumineux, je continuerai dans le suivant, où j'écrirai comment créer mes composants (par exemple, un calendrier ou inputSearch) et les valider, comment les associer à redux et plus encore.

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


All Articles