2.2.6 • Published 5 years ago

react-validation-boo v2.2.6

Weekly downloads
2
License
ISC
Repository
-
Last release
5 years ago

react-validation-boo

Компонент для обеспечения проверки сложных динамических форм на React.

react-validation-boo предоставляет вам набор правил и компонентов для валидации форм.

Примеры использования

Для начала создадим компонент и подключим к нему валидацию.

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

Давайте разберём этот код. Начнём с функции connect, в неё мы передаём наши правила валидации и другие дополнительные параметры. Вызвав этот метод мы получаем новую функцию в которую передаём наш компонент(MyForm), чтобы он получил в props необходимые методы работы с валидацией форм.

В функции render нашего компонента мы возвращаем компонент Form который соединяем с правилами валидации connect={this.props.connect} Эта необходимая конструкция для того чтобы Form знал как валидировать вложенные компоненты.

Input <Input type="text" name="name" /> поле ввода которое мы будем проверять, правила проверки мы передали connect в свойстве rules. В нашем случае это name не должно быть пустым(required). Также мы в connect передали middleware: logger, для того чтобы в консоли увидеть как происходит валидация

В props нашего компонента мы получили набор функций 1. vBoo.isValid() - возвращает true, если все компоненты ввода прошли валидацию 2. vBoo.hasError(name) - возвращает true, если компонент со свойством name не валидин 3. vBoo.getError(name) - для компонента со свойством name, возвращает текст ошибки

Управление валидацией в зависимости от языка

В функцию connect можно передать язык lang, для того чтобы можно было менять правила валидации в зависимости от того на каком языке мы работаем, а также выводить ошибки на нужном языке.

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

В данном примере чекбокс remember на русском язык обязательно должен быть установлен required, а на других он всегда валиден valid.

Также мы передали в connect функцию labels(lang), которая возвращает название полей в читаемом виде.

В props вашего компонента, есть функция getLabel(name), которая возвращает значение переданное функцией labels или если такого значения нет, то возвращает name.

Базовые компоненты

Form форма в которой находятся поля ввода для валидации

Input стандартное html input поля ввода к которой прикрепляются правила валидации type!=(radio|checkbox)

InputRadio стандартное html input(type=radio)

InputCheckbox стандартное html input(type=checkbox)

Select стандартное html select

area стандартное html 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" multiple>
                    <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);
Правила валидации

valid, required, email, number, url, string-length, not-empty-array На данный момент библиотека имеет небольшой набор правил валидаций, поэтому эти правила вам прийдётся писать самим.

Для того чтобы написать правило необходимо создать класс, который будет унаследован от класса validator

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;

Давайте теперь подключим наш валидатор к форме

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);
Сценарии

Давайте рассмотрим случаи когда нам необходимо менять правила валидации в зависимости от действий производимых на форме.

По умолчанию у нас сценарий который называется default, в правилах мы можем прописать при каком сценарии проводить данную валидацию. Если сценарий не указан, то валидация будет выполняться для всех сценариев.

rules = () => ([
    [
        'name',
        'required',
        {
            error: '%name% не должно быть пустым'
        }
    ],
    [
        'name',
        'myValidator',
        {
            scenario: ['default', 'scenario1']
        }
    ],
    [
        'email',
        'email',
        {
            scenario: 'scenario1'
        }
    ]
])

Через свойство props нашего компонента передаются функции: 1. vBoo.setScenario(scenario) Устанавливает сценарий scenario, может быть как строка так и массив, если у нас активны сразу несколько сценариев 2. vBoo.getScenario() Возвращает текущий сценарий или массив сценариев 3. vBoo.hasScenario(name) Показывает установлен ли сейчас данный сценарий, name строка

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" multiple>
                    <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);
Создание компонентов.

По мимо использования стандартных компонентов вы можете использовать свои. Создадим компонент InputBlock, который будет в себе содержать label и вывод ошибок.

import React, {Component} from 'react';
import {Input} from 'react-validation-boo';

class InputBlock extends Input {
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <input {...this.props} onChange={this.change} onBlur={this.blur} />
                {this.getError()}
            </div>
        );
    }
}

export default InputBlock;

Заменим все стандартные элементы на блоки

import React from 'react';
import {InputCheckbox} from 'react-validation-boo';

class InputCheckboxBlock extends InputCheckbox {
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <input {...this.props} type="checkbox" onChange={this.change} />
                {this.getError()}
            </div>
        );
    }
}

export default InputCheckboxBlock;
import React, {Component} from 'react';
import {Textarea} from 'react-validation-boo';

class TextareaBlock extends Textarea {
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <textarea {...this.props} onChange={this.change} onBlur={this.blur} />
                {this.getError()}
            </div>
        );
    }
}

export default TextareaBlock;

Напишем блок для Select так чтобы опции можно было передавать не только через теги option, а ещё через массив.

let genderOptions = [
    {
        value: '',
        label: 'Ваш пол?',
        disabled: true
    },
    {
        value: 1,
        label: 'Мужской'
    },
    {
        value: 2,
        label: 'Женский'
    }
];
<SelectBlock name="gender" options={genderOptions} />

<SelectBlock name="gender">
    <option disabled>Ваш пол</option>
    <option value="1">Мужской</option>
    <option value="2">Женский</option>
</SelectBlock>
import React, {Component} from 'react';
import {Select} from 'react-validation-boo';

class SelectBlock extends Select {
    componentWillMount() {
        this.children = this.props.options ? this.__getOptions(): this.props.children;
    }
    componentWillReceiveProps(nextProps) {
        if(this.props.value !== nextProps.value) {
            this.props.vBoo.change(nextProps.value);
        }
    }
    __getOptions() {
        return this.props.options.map((item) => {
            return <option value={item.value} disabled={item.disabled}>{item.label}</option>;
        });
    }
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <select {...this.props} onChange={this.change}>
                    {this.children}
                </select>
                {this.getError()}
            </div>
        );
    }
}

export default SelectBlock;

Осталось реализовать компонент InputRadioGroupBlock, мы будем его реализовывать не на базе существующих, а с нуля. Компонент Form библиотеки react-validation-boo ищет среди своих потомков элементы с полем name и в них через propsпередаёт объект vBoo в этом объекте и находятся все необходимые методы для работы с валидацией компонента. Для начала напишем как мы будем использовать компонент.

let familyRadioList = [
    {
        value: 1,
        label: 'холост'
    },
    {
        value: 2,
        label: 'сожительство'
    },
    {
        value: 3,
        label: 'брак'
    }
];
<InputRadioGroupBlock name="familyStatus" items={familyRadioList} />
import React, {Component} from 'react';

export default class InputRadioGroupBlock extends Component {
    state = {
        value: ''
    };
    componentDidMount() {
        this.setState({value: this.props.value});
        this.props.vBoo.mount(this.state.value);
    }
    componentWillUnmount() {
        this.props.vBoo.unMount();
    }
    componentWillReceiveProps(nextProps) {
        let value = nextProps.value;
        if(this.props.value !== nextProps.value) {
            this.setState({value});
            this.props.vBoo.change(value);
        }
    }
    getOptions() {
        return this.props.items.map(item => {
            let checked = (this.state.value||'').toString()===(item.value||'').toString();
            return <div key={item.value}>
                       <input type="radio" name={this.props.name} value={item.value} checked={checked} onChange={this.change} />
                       <label>{item.label}</label>
                   </div>;
        });
    }
    change = (event) => {
        let value = event.target.value;
        this.props.onChange && this.props.onChange(event);
        this.setState({value});
        this.props.vBoo.change(value);
    };
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <div>{this.props.vBoo.getLabel()}:</div>
                {this.getOptions()}
                {this.getError()}
            </div>
        );
    }
}
2.2.6

5 years ago

2.2.5

5 years ago

2.2.4

5 years ago

2.2.3

5 years ago

2.2.2

5 years ago

2.2.1

5 years ago

2.1.2

5 years ago

2.1.1

5 years ago

2.1.0

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

0.0.3

5 years ago

0.0.2

5 years ago

0.0.1

5 years ago