组件规范 前端代码

Field 表单通用工具

辅助获取表单数据的工具

何时使用#

在需要校验、获取多个表单数据的时候使用,可以提高数据获取效率。

注意事项#

使用了Field init 过的组件,请勿再定义 ref value onChange 事件。

Form已经和Field数据获取自动校验提示方面做了深度优化,建议在Form中使用Field, 请查看 Form demo。

API#

初始化#

let myfield = new Field(this [,options]);
参数说明类型可选值默认值
this传入调用class的thisReact.Component必须设置
options一些事件配置, 详细参数如下React.Component非必须

options 配置项

参数说明类型可选值默认值
onChange所有组件的change都会到达这里[setValue不会触发该函数]Function(name,value)

API接口#

new之后的对象提供的api接口 (例:myfield.getValues())(set 开头的api函数不要在render里面操作)

参数说明类型可选值默认值
init初始化每个组件,详细参数如下Function(name:string, option:object)
getValues获取一组输入控件的值,如不传入参数,则获取全部组件的值Function([names: string[]])
getValue获取单个输入控件的值Function(name: string)
setValues设置一组输入控件的值(会触发render,请遵循react时机使用)Function(obj: object)
setValue设置单个输入控件的值 (会触发render,请遵循react时机使用)Function(name: string, value)
validate校验并获取一组输入域的值与 ErrorFunction([names: string[]], [options: object], callback: Function(errors, values))
getError获取单个输入控件的 ErrorFunction(name: string)
setError设置单个输入控件的 ErrorFunction(name: string, errors:string/array[string])
setErrors设置一组输入控件的 ErrorFunction(obj: object)
reset重置一组输入控件的值、清空校验Function([names: string[]])
getState判断校验状态Function(name: string)'error' 'success' 'validating' ''''
remove删除某一个或者一组控件的数据,删除后与之相关的validate/value都会被清空Function(name: string/string[])

init#

init(name,options)
参数说明类型可选值默认值
name必填输入控件唯一标志string
options.valueName组件值的属性名称,如 Checkbox 的是 checked,Input是 valuestring'value'
options.initValue组件初始值,如果不填会自动读取组件的defaultValueany
options.trigger触发取数据的方法string'onChange'
options.rules校验规则array/object
options.normalize自定义从onChange参数获取value的方式,一般不需要设置Function(value,e)

返回值

{id,value,onChange}

rules#

{
    rules:[{ required: true }]
}

多个rule

{
    rules:[{required:true,trigger:'onBlur'},{pattern:/abcd/,message:'abcd不能缺'},{validator:(rule, value, callback)=>{callback('出错了')}}]
}
参数说明类型可选值默认值
required不能为空 (不能和pattern同时使用)Booleantrue
message出错时候信息string
type被校验数据类型, 详细文档见stringstring/array/url/email/...string
pattern校验正则表达式正则表达式(例如:/^[0-9]*$/
len长度校验,如果max、mix混合配置,len的优先级最高number
min最小值number
max最大值number
whitespace允许空白字符Boolean
validator自定义校验Function(rule,value,callback)

更加详细的rules建议规则见async-validator的rules项

代码演示

import Button from 'blue/lib/button';
import DatePicker from 'blue/lib/date-picker';
import Upload from 'blue/lib/upload';
import Field from 'blue/lib/field';

import 'blue/lib/button/index.scss';
import 'blue/lib/date-picker/index.scss';
import 'blue/lib/upload/index.scss';

class App extends React.Component {

    field = new Field(this);

    normFile(list) {
        if (Array.isArray(list)) {
            return list;
        }
        return list && list.fileList;
    }

    normDate(date, strdate) {
        console.log('normDate', date, strdate);
        return strdate;
    }

    render() {
        const init = this.field.init;

        return (<div>

            <DatePicker {...init('datepicker', {
                normalize: this.normDate
            })} />
            <br/><br/>

            <Upload listType="text" {...init('upload', {
                normalize: this.normFile
            })} />
            <br/><br/>

            <Button type="primary" onClick={() => {
                console.log(this.field.getValues());
            }}>getValues</Button>
        </div>);
    }
}


ReactDOM.render(<App/>, mountNode);

normalize: 自定义从组件的onChange参数获取value的方式

import Input from 'blue/lib/input';
import Select from 'blue/lib/select';
import Range from 'blue/lib/range';
import Field from 'blue/lib/field';

import 'blue/lib/input/index.scss';
import 'blue/lib/select/index.scss';
import 'blue/lib/range/index.scss';


class App extends React.Component {
    field = new Field(this, {
        onChange: (name, value) => {
            switch (name) {
                case 'input':
                    this.field.setValue('sync', `被改成了: ${value}`);
                    break;
                case 'select':
                    this.field.setValue('sync', `${value} 也来打豆豆了`);
                    break;
                case 'range':
                    this.field.setValue('sync', `坐标 (${value.join(',')}) ready`);
                    break;
            }
        }
    });

    render() {
        const init = this.field.init;
        const layout = {
            marginBottom: 10,
            width: 400
        };

        return (<div>
            <Input placeholder="我在Field的onChange里面做了控制" {...init('input')} style={layout}/><br/>
            <Input placeholder="受控同步" {...init('input')} style={layout}/><br/>

            <Select defaultValue="lucy" style={layout} {...init('select')}>
                <li value="jack">jack</li>
                <li value="lucy">lucy</li>
                <li value="disabled" disabled>disabled</li>
                <li value="hugo">hugo</li>
            </Select><br/>

            <Range style={{...layout, marginTop: 30}} slider={'double'} defaultValue={[20, 40]} scales={10}
                   marks={10}  {...init('range', {trigger: 'onProcess'})}/>
            <br/>

            <hr style={{marginBottom: 10}}/>
            <Input placeholder="我就是被人打的波波, 谁都能控制我" {...init('sync')} style={layout}/><br/>
        </div>);
    }
}

ReactDOM.render(<App/>, mountNode);

统一处理组件的onChange,控制组件之间的关联控制

import Input from 'blue/lib/input';
import Button from 'blue/lib/button';
import Field from 'blue/lib/field';

import 'blue/lib/input/index.scss';
import 'blue/lib/button/index.scss';


class App extends React.Component {
    field = new Field(this);

    render() {
        const init = this.field.init;
        return (<div>
            <Input  {...init('input', {
                rules: {
                    required: true,
                    pattern: /hello/,
                    message: '我是真正的错误信息,必须填写hello'
                }
            })}  /><br/>
            <span style={{color: 'red'}}>{this.field.getError('input')}</span>

            <br/>
            <Button onClick={() => {
                this.field.setError('input', '设置的错误信息');
            }}>setError</Button>

            <Button onClick={() => {
                this.field.setErrors({input: '设置的错误信息2'});
            }}>setErrors</Button>

            <Button onClick={() => {
                this.field.setErrors({input: ''});
            }}>clear</Button>

            <br/><br/>
            <Input  {...init('input2')}  /><br/>
            <span style={{color: 'red'}}>{this.field.getError('input2')}</span><br/>

            <Button onClick={() => {
                this.field.setError('input2', '设置的错误信息onChange后会被去除');
            }}>setError</Button>
        </div>);
    }
}


ReactDOM.render(<App/>, mountNode);

自己控制组件的errors

import Button from 'blue/lib/button';
import Checkbox from 'blue/lib/checkbox';
import Radio from 'blue/lib/radio';
import Switch from 'blue/lib/switch';
import Field from 'blue/lib/field';

import 'blue/lib/button/index.scss';
import 'blue/lib/checkbox/index.scss';
import 'blue/lib/radio/index.scss';
import 'blue/lib/switch/index.scss';


class App extends React.Component {

    field = new Field(this);

    render() {
        const init = this.field.init; //如果使用简写不能缺少bind

        return (<div>
            <Radio {...init('radio', {valueName: 'checked'})} > valueName = checked</Radio>
            <br/>
            <Checkbox defaultChecked {...init('checkbox', {valueName: 'checked'})} >
                默认值为defaultChecked
            </Checkbox>
            <br/>
            <Switch {...init('switch', {valueName: 'checked'})} style={{marginTop: 10, marginBottom: 10}}/>
            <br/>

            <Button type="primary" onClick={() => {
                console.log(this.field.getValues());
            }}>getValues</Button>
            <Button onClick={() => {
                this.field.setValues({
                    radio: true,
                    switch: true,
                    checkbox: false
                });
            }}> setValues </Button>
            <Button onClick={() => {
                this.field.reset();
            }}>reset</Button>
        </div>);
    }
}

ReactDOM.render(<App/>, mountNode);

valueName的默认值为value,如果为其他需要用valueName指定

import Input from 'blue/lib/input';
import Button from 'blue/lib/button';
import Checkbox from 'blue/lib/checkbox';
import Field from 'blue/lib/field';

import 'blue/lib/input/index.scss';
import 'blue/lib/button/index.scss';
import 'blue/lib/checkbox/index.scss';
import 'blue/lib/radio/index.scss';


const CheckboxGroup = Checkbox.Group;

const list = [
    {
        value: 'apple',
        label: '苹果'
    }, {
        value: 'pear',
        label: '梨'
    }, {
        value: 'orange',
        label: '橙子'
    }
];

class App extends React.Component {
    state = {
        checkboxStatus: true
    }
    field = new Field(this);

    isChecked(rule, value, callback) {
        if (!value) {
            callback('没有勾选同意协议');
        } else {
            callback();
        }
    }

    userName(rule, value, callback) {
        if (value === 'frank') {
            setTimeout(() => callback('名称已经存在'), 200);
        } else {
            setTimeout(() => callback(), 200);
        }
    }

    render() {
        const init = this.field.init; //如果使用简写不能缺少bind
        return (<div>
            <Input defaultValue="删除试试" {...init('input', {rules: {required: true}})} />
            {this.field.getError('input') ?
                <span style={{color: 'red'}}>{this.field.getError('input').join(',')}</span> : ''}

            <br/>
            <br/>

            <Input placeholder="离开焦点onBlur" {...init('input1', {
                rules: {
                    required: true,
                    message: '不能为空',
                    trigger: ['onBlur', 'onChange']
                }
            })} />
            {this.field.getError('input1') ?
                <span style={{color: 'red'}}>{this.field.getError('input1').join(',')}</span> : ''}

            <br/>
            <br/>

            <Input defaultValue="" placeholder="填写frank" {...init('username', {
                rules: {
                    validator: this.userName,
                    trigger: ['onBlur', 'onChange']
                }
            })} />
            {this.field.isValidating('username') ? '正在校验中...' : ''}
            {this.field.getError('username') ?
                <span style={{color: 'red'}}>{this.field.getError('username').join(',')}</span> : ''}

            <br/>
            <br/>

            是否同意以上协议:
            <Checkbox  {...init('checkbox', {
                valueName: 'checked',
                rules: {validator: this.isChecked}
            })} />
            {this.field.getError('checkbox') ?
                <span style={{color: 'red'}}>{this.field.getError('checkbox').join(',')}</span> : ''}

            <br/>
            <br/>

            <Input multiple maxLen={10} defaultValue="字符串长度在3-10之间" {...init('textarea', {
                rules: [{
                    required: true,
                    min: 3,
                    max: 10
                }]
            })} />
            {this.field.getError('textarea') ?
                <span style={{color: 'red'}}>{this.field.getError('textarea').join(',')}</span> : ''}

            <br/>
            <br/>

            {this.state.checkboxStatus ? <div>
                数组类型的数据校验:
                <CheckboxGroup dataSource={list}  {...init('checkboxgroup', {
                    rules: {
                        required: true,
                        type: 'array',
                        message: '必须勾选一个吧'
                    }
                })} style={{marginBottom: 10}}/>
                {this.field.getError('checkboxgroup') ?
                    <span style={{color: 'red'}}>{this.field.getError('checkboxgroup').join(',')}</span> : ''}
            </div> : null}

            <br/>
            <br/>

            <Button type="primary" onClick={() => {
                this.field.validate((errors, values) => {
                    console.log(errors, values);
                });
            }}>validate</Button>
            <Button onClick={() => {
                this.field.reset();
            }}>reset</Button>

            <Button onClick={() => {
                if (this.state.checkboxStatus) {
                    this.setState({checkboxStatus: false});
                    this.field.remove('checkboxgroup');
                } else {
                    this.setState({checkboxStatus: true});
                }

            }}>{this.state.checkboxStatus ? '删除CheckboxGroup' : '打开CheckboxGroup'}</Button>
        </div>);
    }
}


ReactDOM.render(<App/>, mountNode);

校验的错误信息需要用getError获取

注意:Form和Field做了深度结合,在Form中使用Field,错误信息不需getError获取会自动展现。


import Input from 'blue/lib/input';
import Button from 'blue/lib/button';
import Field from 'blue/lib/field';

import 'blue/lib/input/index.scss';
import 'blue/lib/button/index.scss';

import { combineReducers, createStore } from 'redux';
import { Provider, connect } from 'react-redux';

function formReducer(state = {email: 'frankqian@qq.com'}, action) {
    switch (action.type) {
        case 'save_fields':
            return {
                ...state,
                ...action.payload,
            };
        default:
            return state;
    }
}

class Demo extends React.Component {
    field = new Field(this, {
        onChange: (name, value) => {
            console.log('onChange', name, value);
            this.field.setValue('newlen', value.length);
            this.props.dispatch({
                type: 'save_fields',
                payload: {
                    [name]: value
                }
            });
        }
    });

    setEmail() {
        this.props.dispatch({
            type: 'save_fields',
            payload: {
                email: 'qq@gmail.com'
            },
        });
    }

    componentWillReceiveProps(nextProps) {
        this.field.setValues({
            email: nextProps.email,
            newlen: nextProps.email.length
        });
    }

    render() {
        const init = this.field.init;

        let newLen = init('newlen', {initValue: this.props.email.length});

        return <div>
            <Input defaultValue={this.props.email} {...init('email', {
                rules: [
                    {required: true, type: 'email', message: '用户名至少为 5 个字符'},
                ]
            })} />
            现在的长度是:{newLen.value}
            <p>email: {this.props.email}</p>
            <Button onClick={this.setEmail.bind(this)}>set</Button>
        </div>;
    }
}


Demo = connect((state) => {
    return {
        email: state.formReducer.email,
    };
})(Demo);


const store = createStore(combineReducers({
    formReducer,
}));

class App extends React.Component {
    render() {
        return (<Provider store={store}>
            <div>
                <Demo />
            </div>
        </Provider>);
    }
}


ReactDOM.render(<App/>, mountNode);

在redux中使用, 在componentWillReceiveProps更新

import Input from 'blue/lib/input';
import Button from 'blue/lib/button';
import { Group as CheckboxGroup } from 'blue/lib/checkbox';
import Radio, { Group as RadioGroup } from 'blue/lib/radio';
import Select from 'blue/lib/select';
import Range from 'blue/lib/range';
import Field from 'blue/lib/field';

import 'blue/lib/input/index.scss';
import 'blue/lib/button/index.scss';
import 'blue/lib/checkbox/index.scss';
import 'blue/lib/radio/index.scss';
import 'blue/lib/select/index.scss';
import 'blue/lib/range/index.scss';

const list = [
    {
        value: 'apple',
        label: '苹果'
    }, {
        value: 'pear',
        label: '梨'
    }, {
        value: 'orange',
        label: '橙子'
    }
];
const layout = {
    marginBottom: 10,
    width: 400
};

class App extends React.Component {
    field = new Field(this);

    render() {
        const init = this.field.init;

        return (<div>
            <Input defaultValue="init" {...init('input')} style={layout}/><br/>
            <Select defaultValue="lucy" {...init('select')} style={layout}>
                <li value="jack">jack</li>
                <li value="lucy">lucy</li>
                <li value="disabled" disabled>disabled</li>
                <li value="hugohua">hugohua</li>
            </Select><br/>

            <Range style={{...layout, marginTop: 30}} slider={'double'} defaultValue={[20, 40]} scales={10}
                   marks={10}  {...init('range', {trigger: 'onProcess'})}/>
            <div style={{marginBottom: 10}}>
                <CheckboxGroup dataSource={list}  {...init('checkboxgroup')} />
            </div>

            <div style={{marginBottom: 10}}>
                <RadioGroup {...init('radiogroup')} defaultValue="b">
                    <Radio value="a">A</Radio>
                    <Radio value="b">B</Radio>
                    <Radio value="c">C</Radio>
                    <Radio value="d">D</Radio>
                </RadioGroup>
            </div>

            <Button type="primary" onClick={() => {
                console.log(this.field.getValues());
            }}>getValues</Button>
            <Button onClick={() => {
                console.log(this.field.getValue('input'));
            }}>getValue</Button>
            <Button onClick={() => {
                this.field.setValues({
                    input: '通过setValues设置',
                    select: 'hugohua',
                    range: [30, 50],
                    checkboxgroup: ['orange'],
                    radiogroup: 'd'
                });
            }}>setValues</Button>
            <Button onClick={() => {
                this.field.reset();
            }}>reset</Button>
            <Button onClick={() => {
                this.field.reset(['input']);
            }}>reset(['input'])</Button>
        </div>);
    }
}


ReactDOM.render(<App/>, mountNode);