Пару лет назад я увидел на Хабре статью про LIVR и с тех пор использую библиотеку на всех проектах. С переходом на React я адаптировал для валидации ее же, т.к. существующие решения не предлагали гибкости которой мне хотелось. Свое решение я уже использую на двух проектах и решил выложить в npm — может кому-то еще оно покажетсяя удобным.
Пакет называется react-livr-validation.
Пример базового использования:
Компонент принимает валидационную схему и первоначальные данные(если данные не валидны, кнопка submit сразу будет неактивна), так же можно передать custom rules и aliased rules:
Обертка ValidationInput добавляет в инпут свои обработчики событий, на которые будет происходить валидация. По умолчанию это события change, blur, keyup.
Есть возможность реализовать свою обертку — пакет экпортит HOC, который прокидывает в пропсы api
Пакет называется react-livr-validation.
Пример базового использования:
import React from 'react';
import Validation, {DisabledOnErrors, ValidationInput} from 'react-livr-validation';
const schema = {
login: ['required', 'not_empty'],
password: ['required', 'not_empty']
};
const data = {
login: '',
password: ''
};
export default function() {
return (
<Validation
data={data}
schema={schema}
>
<form>
<ValidationInput name="login" >
<input name="login" />
</ValidationInput>
<ValidationInput name="password" >
<input name="password" type="password" />
</ValidationInput>
<DisabledOnErrors>
<input type="submit" />
</DisabledOnErrors>
</form>
</Validation>
);
}
Компонент принимает валидационную схему и первоначальные данные(если данные не валидны, кнопка submit сразу будет неактивна), так же можно передать custom rules и aliased rules:
const customRules = {
alpha_chars: function() {
return function(value) {
if (typeof value === 'string') {
if (!/[a-z,A-Z]+/.test(value)) {
return 'WRONG_FORMAT';
}
}
};
}
};
const aliasedRules = [
{
name: 'strong_password',
rules: { min_length: 6 },
error: 'TOO_SHORT'
}
];
<Validation
data={data}
schema={schema}
rules={customRules}
aliasedRules={aliasedRules}
>
// ... form
</Validation>
Обертка ValidationInput добавляет в инпут свои обработчики событий, на которые будет происходить валидация. По умолчанию это события change, blur, keyup.
<ValidationInput name="password" valicateOnEvents={['change', 'blur', 'keyUp']} >
<input name="password" type="password" />
</ValidationInput>
Есть возможность реализовать свою обертку — пакет экпортит HOC, который прокидывает в пропсы api
// @flow
import React, {Component} from 'react'
import {ValidationComponent} from 'react-livr-validation'
import get from 'lodash/get'
import noop from 'lodash/noop'
import compose from 'ramda/src/compose'
import styled from 'styled-components'
type DataChunk = {
name: string,
value: any
}
type State = {
touched: boolean
}
type Props = {
// will be passed by HOC
setData: (data: DataChunk) => void,
getError: (name: string) => ?string,
getErrors: () => Object,
className: string, // for the error block
style: Object // for the error block
errorCodes: Object,
name: string,
field: string
}
class NestedError extends Component {
props: Props;
isTouched() {
const {children} = this.props;
return get(children, 'props.value')
}
state: State = {
touched: this.isTouched()
}
setTouched() {
this.setState({
touched: true
})
}
cloneElement() {
const {children} = this.props;
const onBlur = get(children, 'props.onBlur', noop);
return React.cloneElement(
children,
{
onBlur: compose(this.setTouched, onBlur)
}
)
}
render() {
const {touched} = this.state;
const {
children,
field,
name,
getError,
errorCodes,
style,
className
} = this.props;
const errors = getErrors();
const error = get(errors, `${field}`.${name});
return (
<div>
{touched ? children : this.cloneElement()}
{error &&
<Error
className={className}
style={style}
>
{errorCodes[error] || error}
</Error>
}
</div>
);
}
}
const Error = styled.div`
color: red;
`;
export default ValidationComponent(NestedError)
Комментарии (7)
turbo_exe
10.09.2017 22:30ваша концепция LIVIR очень похожа на то, что я хотел от валидации на JS с год назад. я ничего стоящего не нашёл тогда и в итоге написал свой велосипед который очень похож на ваш, но работает только на js и обладает (пока) меньшим функционалом.
вопрос: можно ли в LIVIR сделать так чтобы поле валидировалось только при определённых условиях? например, валидировать email только если пользователь подписался на рассылку. в моём велосипеде это делается как-то так:
const { validate, util: { when }, rules: { isBoolean, isEmail } } = require('yeval'); const optedInForNewsletter = (value, data) => data.optedInForNewsletter === true; validate( // declare rules as first argument { optedInForNewsletter: isBoolean, email: when(optedInForNewsletter, isEmail), }, // pass data as second argument { optedInForNewsletter: true } ) .then(errors => { console.log(errors); // { email: 'Must be a valid email address' } });
koorchik
11.09.2017 10:14+1У нас в проектах мы сделали правило required_if
И пишем так:
{ optedInForNewsletter: {one_of: [1, 0]}, email: [{required_if: 'optedInForNewsletter'}, 'email'], }
one_more Автор
11.09.2017 11:31Как вариант можно еще динамически менять правила валидации, если правил много и применять надо только при условии.
import React, {Component} from 'react'; import Validation, {DisabledOnErrors, ValidationInput} from 'react-livr-validation'; const data = { login: '', email: '' }; class App extends Component { state = { withEmail: false }; toggleWithEmail = ({target: {checked}}) => { this.setState({ withEmail: checked }) }; render() { const {withEmail} = this.state; const schema = { login: ['required', 'not_empty'], email: withEmail ? ['required', 'not_empty', 'email'] : [] }; return ( <div> <Validation data={data} schema={schema} > <form> <ValidationInput name="login"> <input name="login"/> </ValidationInput> <ValidationInput name="email"> <input name="email" type="email"/> </ValidationInput> <div> <label htmlFor="subscribe">subscribe</label> <input id="subscribe" type="checkbox" onChange={this.toggleWithEmail} /> </div> <DisabledOnErrors> <input type="submit"/> </DisabledOnErrors> </form> </Validation> </div> ); } }
argonavtt
Вы
</Error
не закрыли в последнем примереone_more Автор
поправил