@piscium2010/v-form

1.4.2 • Public • Published

v-form

This is not a form, instead it is only a form shell of which the single responsibility is validating and providing error messages. Capable of definding flexiable rules and validating multi fields at a time.

Install

npm i -S @piscium2010/v-form

Use

v-form is a shell, so it works well with any UI libraries like antd, office fabric and material ui. Please find following examples.

More about validation v-rule

Ant Design

import React from 'react'
import { Button, Select, Input, DatePicker } from 'antd'
import VForm, { v } from 'v-form'

const Field = VForm.fieldFactory(({ name, message, children }) => {
    return (
        <div className={message ? 'has-error' : ''} style={{ marginBottom: 15 }}>
            {children}
            <div className='error-msg'>{message}</div>
        </div>
    )
})

const validation = v.create({
    email: v.expect('Required').expect('Should be email', c => v.isEmail(c.email)),
    marriage: [
        v.expect('Required'),
        v.when('marriage', c => c.marriage === 'single').validate('marriage_date')
    ],
    marriage_date: v.when('marriage', c => c.marriage === 'married').expect('Required when married')
})

const style = { margin: 'auto', width: 380 }

export default class Form extends React.Component {
    state = { values: {} }

    update = value => {
        const { values } = this.state
        validation.test(value, values /* context */)
        this.setState({ values: { ...values, ...value } })
    }

    onSubmit = () => {
        const { values } = this.state
        const result = validation.testAllRules(values)
        if (result.pass) {
            window.alert('pass')
        }
    }

    render() {
        return (
            <div style={style}>
                <p>Validation rules as:</p>
                <ul>
                    <li>Email is required with correct format</li>
                    <li>Marriage is required</li>
                    <li>Date of marriage is required only when married</li>
                </ul>
                <VForm validation={validation}>
                    <Field name='email'>
                        <Input
                            placeholder='Email'
                            style={style}
                            onChange={evt => this.update({ email: evt.target.value })}
                        />
                    </Field>
                    <Field name='marriage'>
                        <Select
                            placeholder='Marriage'
                            style={style}
                            onChange={value => this.update({ marriage: value })}
                        >
                            <Select.Option value="single">Single</Select.Option>
                            <Select.Option value="married">Married</Select.Option>
                        </Select>
                    </Field>
                    <Field name='marriage_date'>
                        <DatePicker
                            placeholder='Date of Marriage'
                            style={style}
                            onChange={(m, d) => this.update({ marriage_date: d })}
                        />
                    </Field>
                    <Button type="primary" onClick={this.onSubmit}>Submit</Button>
                </VForm>
            </div>
        )
    }
}

live demo

Office Fabric

import React from 'react'
import { PrimaryButton, DatePicker, DayOfWeek, TextField, Dropdown } from 'office-ui-fabric-react'
import { initializeIcons } from '@uifabric/icons'
import VForm, { v } from 'v-form'

initializeIcons()

const options = [
    { key: 'single', text: 'Single' },
    { key: 'married', text: 'Married' }
]

const Field = VForm.fieldFactory(({ name, message, children }) => {
    const { Children, cloneElement } = React
    return (
        <div>
            {Children.map(children, c => cloneElement(c, { errorMessage: message }))}
        </div>
    )
})

const DateField = VForm.fieldFactory(({ name, message, children }) => {
    const errorStyle = { paddingTop: 5, color: '#A4262c', fontSize: 12, fontFamily: `"Segoe UI"` }

    //add class here to change input border color to red when message !== ''
    return (
        <div>
            {children}
            <div style={errorStyle}>{message}</div>
        </div>
    )
})

const validation = v.create({
    email: v.expect('Required').expect('Should be email', c => v.isEmail(c.email)),
    marriage: [
        v.expect('Required'),
        v.when('marriage', c => c.marriage === 'single').validate('marriage_date')
    ],
    marriage_date: v.when('marriage', c => c.marriage === 'married').expect('Required when married')
})

export default class Form extends React.Component {
    state = { values: {} }

    update = value => {
        const { values } = this.state
        validation.test(value, values/* context */)
        this.setState({ values: { ...values, ...value } })
    }

    onSubmit = () => {
        const { values } = this.state
        const result = validation.testAllRules(values)
        if (result.pass) {
            window.alert('pass')
        }
    }

    render() {
        return (
            <div style={{ width: 400, margin: 'auto' }}>
                <p>Validation rules as:</p>
                <ul>
                    <li>Email is required with correct format</li>
                    <li>Marriage is required</li>
                    <li>Date of marriage is required only when married</li>
                </ul>
                <VForm validation={validation}>
                    <Field name='email'>
                        <TextField
                            label="Email"
                            onChange={(evt, value) => this.update({ email: value })}
                        />
                    </Field>
                    <Field name='marriage'>
                        <Dropdown
                            label="Marriage"
                            options={options}
                            onChange={(evt, option) => this.update({ marriage: option.key })}
                        />
                    </Field>
                    <DateField name='marriage_date'>
                        <DatePicker
                            value={this.state.values.marriage_date}
                            label="Date of Marriage"
                            firstDayOfWeek={DayOfWeek.firstDayOfWeek}
                            onSelectDate={d => this.update({ marriage_date: d })}
                        />
                    </DateField>
                    <PrimaryButton text='Submit' onClick={this.onSubmit} />
                </VForm>
            </div>
        )
    }
}

live demo

Material UI

import React, { useState } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import Button from '@material-ui/core/Button'
import TextField from '@material-ui/core/TextField'
import Select from '@material-ui/core/Select'
import MenuItem from '@material-ui/core/MenuItem'
import InputLabel from '@material-ui/core/InputLabel'
import { MuiPickersUtilsProvider, KeyboardDatePicker } from '@material-ui/pickers'
import DateFnsUtils from '@date-io/date-fns'
import VForm, { v } from 'v-form'

const useStyles = makeStyles(theme => ({
    textField: { width: 300, marginBottom: 5 },
    select: { width: 300, marginBottom: 5 },
    picker: { width: 300, marginBottom: 5 }
}))

const Field = VForm.fieldFactory(({ name, message, children }) => {
    const { Children, cloneElement } = React
    const hasError = message ? true : false
    const errorStyle = { color: '#f44336', fontSize: 12, marginBottom: 15 }
    return (
        <div>
            {Children.map(children, c => cloneElement(c, { error: hasError }))}
            <div style={errorStyle}>{message}</div>
        </div>
    )
})

const validation = v.create({
    email: v.expect('Required').expect('Should be email', c => v.isEmail(c.email)),
    marriage: v.expect('Required'),
    birth_date: v.expect('Required')
        .expect(
            'Seriouly? Just born?',
            c => c.birth_date.getFullYear() < new Date().getFullYear()
        )
})

export default function Form() {
    const [values, setValues] = useState({ email: '', marriage: '', birth_date: new Date() })

    const update = value => {
        validation.test(value, values/* context */)
        setValues({ ...values, ...value })
    }

    const onSubmit = () => {
        const result = validation.testAllRules(values)
        if (result.pass) {
            window.alert('pass')
        }
    }

    const classes = useStyles();
    const style = { margin: 'auto', width: 380 }

    return (
        <MuiPickersUtilsProvider utils={DateFnsUtils}>
            <div style={style}>
                <p>Validation rules as:</p>
                <ul>
                    <li>Email is required with correct format</li>
                    <li>Marriage is required</li>
                    <li>Birth date year can not be current year</li>
                </ul>
                <VForm validation={validation}>
                    <InputLabel>Email</InputLabel>
                    <Field name='email'>
                        <TextField
                            className={classes.textField}
                            onChange={evt => update({ email: evt.target.value })}
                        />
                    </Field>
                    <InputLabel>Marriage</InputLabel>
                    <Field name='marriage'>
                        <Select
                            className={classes.select}
                            value={values.marriage}
                            onChange={evt => update({ marriage: evt.target.value })}
                        >
                            <MenuItem value={'single'}>Single</MenuItem>
                            <MenuItem value={'married'}>Married</MenuItem>
                        </Select>
                    </Field>
                    <InputLabel>Birth Date</InputLabel>
                    <Field name='birth_date'>
                        <KeyboardDatePicker
                        className={classes.picker}
                            format="MM/dd/yyyy"
                            value={values.birth_date}
                            onChange={d => update({ birth_date: d })}
                        />
                    </Field>
                    <Button
                        variant="contained"
                        color="primary"
                        className={classes.button}
                        onClick={onSubmit}
                    >
                        Submit
                    </Button>
                </VForm>
            </div>
        </MuiPickersUtilsProvider>
    )
}

live demo

Package Sidebar

Install

npm i @piscium2010/v-form

Weekly Downloads

28

Version

1.4.2

License

ISC

Unpacked Size

57.1 kB

Total Files

31

Last publish

Collaborators

  • piscium2010