Field 表单辅助工具
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
开发指南
何时使用
涉及到表单数据操作、校验的地方都可以用Field来管理数据。和组件关联后可以自动对表单数据进行回写、读取、校验。
使用注意
使用Field
init
过的组件,value
onChange
必须放在 init 的第三个参数, 否则有可能被 init 覆盖。Form
已经和Field
在数据获取
和自动校验提示
方面做了深度优化,建议在Form
中使用Field
, 请查看 Form demo。initValue 类似组件的 defaultValue 只有在组件第一次render的时候才生效(ajax 异步调用设置 initValue 可能已经错过了第一次render)
autoUnmount 默认打开的,如果需要保留会
自动卸载的组件
数据请关闭此项parseName=true
可以通过getValues
获取到结构化的数据, 但是 getValue 还是必须传完整 key 值
基本使用
class Demo extends React.Component {
field = new Field(this); // 实例创建
onClick = ()=>{
console.log(this.field.getValue('name'));
}
render() {
const init = this.field.init;
// 注意:initValue只会在组件第一次初始化的时候被赋值,如果你是异步赋值请用setValue
return <div>
<Input {...init('name',{initValue:'first value'})} />
<button onClick={this.onClick>获取数据</button>
</div>
}
}
更新数据
事件更新
class Demo extends React.Component {
field = new Field(this);
onClick = ()=>{
this.field.setValue('name', 'newvalue'); // 赋值会自动触发render
}
render() {
const init = this.field.init;
return <div>
<Input {...init('name')} />
<button onClick={this.onClick}>设置数据</button>
</div>
}
}
props更新
class Demo extends React.Component {
field = new Field(this);
// 在组件挂载之前把数据设置进去(可以用initValue替代这种用法)
componentWillMount() {
this.field.setValue('name', 'init Name')
}
// 接收来自props的数据
componentWillReceiveProps(nextProps) {
this.field.setValue('name', nextProps.name)
}
render() {
const init = this.field.init;
return <div>
<Input {...init('name')} />
</div>
}
}
ajax更新
class Demo extends React.Component {
field = new Field(this);
onClick = ()=>{
Ajax({
url:'/demo.json',
success:(json)=>{
// 回调事件中赋值更新
this.field.setValue('name',json.name);
}
});
}
render() {
const init = this.field.init;
return <div>
<Input {...init('name')} />
<button onClick={this.onClick}>设置数据</button>
</div>
}
}
onChange更新监控
两种用法:1. 统一管理
class Demo extends React.Component {
field = new Field(this,{
onChange:(name, value) => {
switch(name) {
case 'name1':
this.field.setValue('name2','value set by name1');
break;
case 'name2':
this.field.setValue('name1','value set by name2');
break;
}
}
});
render() {
const init = this.field.init;
return <div>
<Input {...init('name1')} />
<Input {...init('name2')} />
</div>
}
}
- 各自管理
class Demo extends React.Component {
render() {
const init = this.field.init;
return <div>
<Input {...init('name1',{
props:{
onChange:(v)=>{
this.field.setValue('name2','value set by name1');
}
}
})} />
<Input {...init('name2',{
props:{
onChange:(v)=>{
this.field.setValue('name1','value set by name2');
}
}
})} />
</div>
}
}
详细请查看demo演示 关联控制
API
初始化
let myfield = new Field(this [,options]);
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
this | 传入调用class的this | React.Component | 必须设置 | |
options | 一些事件配置, 详细参数如下 | Object | 非必须 |
options
配置项
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
onChange | 所有组件的change都会到达这里setValue不会触发该函数 | Function(name,value) | |
parseName | 是否翻译init(name) 中的name (getValues会把带. 的字符串转换成对象) | Boolean | false |
forceUpdate | 仅建议PureComponent的组件打开此强制刷新功能,会带来性能问题(500个组件为例:打开的时候render花费700ms, 关闭时候render花费400ms) | Boolean | false |
scrollToFirstError | field.validate的时候滚动到第一个出错的组件, 如果是整数会进行偏移 | Boolean/Number | true |
autoUnmount | 自动删除Unmout元素,如果想保留数据可以设置为false | Boolean | true |
autoValidate | 是否修改数据的时候就自动触发校验, 设为 false 后只能通过 validate() 来触发校验 | Boolean | true |
values | 初始化数据 | Object |
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 | 校验并获取一组输入域的值与 Error | Function([names: String[]], options: Object, callback: Function(errors, values)) | ||
getError | 获取单个输入控件的 Error | Function(name: String) | ||
getErrors | 获取一组输入控件的 Error | Function(name: String) | ||
setError | 设置单个输入控件的 Error | Function(name: String, errors:String/ArrayString) | ||
setErrors | 设置一组输入控件的 Error | Function(obj: Object) | ||
reset | 重置一组输入控件的值、清空校验 | Function([names: String[]]) | ||
resetToDefault | 重置一组输入控件的值为默认值 | Function([names: String[]]) | ||
getState | 判断校验状态 | Function(name: String) | 'error' 'success' 'loading' '' | '' |
getNames | 获取所有组件的key | Function() | ||
remove | 删除某一个或者一组控件的数据,删除后与之相关的validate/value都会被清空 | Function(name: String/String[]) | ||
spliceArray | 删除 name 是数组格式的数据, 并且自动处理其他 name 的数组错位问题 | Function(keyMatch: String, index: Number) |
init
init(name, options, props)
参数 | 说明 | 类型 | 可选值 | 默认值 | |
---|---|---|---|---|---|
name | 必填输入控件唯一标志 | String | |||
options.valueName | 组件值的属性名称,如 Checkbox 的是 checked ,Input是 value | String | 'value' | ||
options.initValue | 组件初始值(组件第一次render的时候才会读取,后面再修改此值无效),类似defaultValue | any | |||
options.trigger | 触发数据变化的事件名称 | String | 'onChange' | ||
options.rules | 校验规则 | Array/Object | |||
options.getValueFromEvent | 自定义从onChange 事件中获取value的方式,一般不需要设置. 详细用法查看demo 自定义数据获取 | Function(value,…args) 参数顺序和组件是完全一致的 | |||
props | 组件自定义的事件可以写在这里 | Object | |||
autoValidate | 是否修改数据的时候自动触发校验单个组件的校验, 设为 false 后只能通过 validate() 来触发校验 | Boolean | true |
返回值
{id,value,onChange}
rules
{
rules:[{ required: true }]
}
多个rule
{
rules:[{required:true,trigger:'onBlur'},{pattern:/abcd/,message:'abcd不能缺'},{validator:(rule, value, callback)=>{callback('出错了')}}]
}
参数 | 说明 | 类型 | 可选值 | 使用类型 |
---|---|---|---|---|
required | 不能为空 | Boolean | true | undefined/null/“”/[] 会触发此规则) |
pattern | 校验正则表达式 | 正则 | ||
minLength | 字符串最小长度 / 数组最小个数 | Number | String/Number/Array | |
maxLength | 字符串最大长度 / 数组最大个数 | Number | String/Number/Array | |
length | 字符串精确长度 / 数组精确个数 | Number | String/Number/Array | |
min | 最小值 | Number | String/Number | |
max | 最大值 | Number | String/Number | |
format | 对常用 pattern 的总结 | String | url、email、tel、number | String |
validator | 自定义校验,(校验成功的时候不要忘记执行 callback() ,否则会校验不返回) | Function(rule,value,callback) | ||
trigger | 触发校验的事件名称 | String/Array | onChange/onBlur/… | onChange |
message | 出错时候信息 | String |
自定义组件接入Field标准
支持受控模式(value+onChange)
必须
value 控制组件数据展现
onChange 组件发生变化时候的回调函数(第一个参数可以给到value)
一次完整操作抛一次onChange事件
建议
比如有Process表示进展中的状态,建议增加APIonProcess
;如果有Start表示启动状态,建议增加APIonStart
value={undefined}
的时候清空数据, field 的 reset 函数会给所有组件下发 undefined 数据建议
componentWillReceiveProps(nextProps) {
if ('value' in nextProps ) {
this.setState({
value: nextProps.value === undefined? []: nextProps.value // 设置组件的被清空后的数值
})
}
}
已知问题
- 为何手动调用
this.field.validate
的时候进不了回调函数? 答: 是不是自定义了validator方法,确保callback
在任何分支下都能被执行到。
代码示例
getValue
setValue
reset
的使用
查看源码在线预览
import { Input, Button, Field } from '@alifd/next';
class App extends React.Component {
field = new Field(this);
onGetValue() {
console.log(this.field.getValue('input'));
}
render() {
const { init, setValue, reset } = this.field;
return (<div className="demo">
<Input {...init('input', {initValue: 'test'})} />
<br/><br/>
<Button type="primary" onClick={this.onGetValue.bind(this)}>getValue</Button>
<Button type="primary" onClick={() => setValue('input', 'set me by click')}>setValue</Button>
<Button onClick={() => reset()}>reset</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
.demo .next-btn {
margin-right: 5px;
}
组件之间的关联控制. onChange
统一管理。
查看源码在线预览
import { Input, Select, Range, Field } from '@alifd/next';
class App extends React.Component {
field = new Field(this, {
onChange: (name, value) => {
console.log(this.field.getValues());
switch (name) {
case 'input':
this.field.setValue('sync', `change to: ${value}`);
break;
case 'select':
this.field.setValue('sync', `${value} is coming`);
break;
case 'range':
this.field.setValue('sync', ` (${value.join(',')}) ready`);
break;
}
}
});
render() {
const {init, getValue} = this.field;
const layout = {
marginBottom: 10,
width: 400
};
return (<div>
<Input placeholder="controlled by onChange" {...init('input')} style={layout}/><br/>
<Input placeholder="under control" {...init('input')} style={layout}/><br/>
<Select style={layout} {...init('select', {initValue: 'lucy'})}>
<Select.Option value="jack">jack</Select.Option>
<Select.Option value="lucy">lucy</Select.Option>
<Select.Option value="disabled" disabled>disabled</Select.Option>
<Select.Option value="hugo">hugo</Select.Option>
</Select><br/>
{
getValue('select') !== 'hugo' ?
<Range
style={{...layout, marginTop: 30}}
slider={'double'} scales={10} marks={10}
{...init('range', {initValue: [20, 40], trigger: 'onProcess'})}
/> : null
}
<br/>
<hr style={{marginBottom: 10}}/>
<Input placeholder="everyone can control me" {...init('sync')} style={layout}/><br/>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
通过 getValueFromEvent
自定义从组件的 Event
获取 value
的方式
查看源码在线预览
import { Button, Input, Field } from '@alifd/next';
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>
<Input {...init('name', { getValueFromEvent: (value) => {
if (value.match(/##/)) {
return value;
} else {
return `## title-${value}`;
}
}})} />
<Button type="primary" onClick={() => {
console.log(this.field.getValues());
}}>getValues</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
自己控制组件的errors
查看源码在线预览
import { Input, Button, Field } from '@alifd/next';
class App extends React.Component {
field = new Field(this);
validate = () => {
console.log(this.field.getErrors());
this.field.validate((error, values) => {
// eslint-disable-next-line no-alert
alert(JSON.stringify(error));
});
};
render() {
const { init, getError, setError, setErrors } = this.field;
return (<div className="demo">
<Input {...init('input', {
rules: [{
required: true,
pattern: /hello/,
message: 'must be hello'
}]
})} /><br/>
<span style={{color: 'red'}}>{getError('input')}</span>
<br/>
<Button onClick={() => {
setError('input', 'set error 1');
}}>setError</Button>
<Button onClick={() => {
setErrors({input: 'set error 2'});
}}>setErrors</Button>
<Button onClick={() => {
setErrors({input: ''});
}}>clear</Button>
<br/><br/>
<Input {...init('input2')} /><br/>
<span style={{color: 'red'}}>{getError('input2')}</span><br/>
<Button onClick={() => {
setError('input2', 'errors will be removed by onChange and shown on validate');
}}>setError</Button>
<Button onClick={this.validate}>
validate</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
.demo .next-btn {
margin-right: 5px;
}
校验的错误信息需要用getError
获取
注意
:Form 和 Field 做了深度结合,在 Form 中使用Field,错误信息不需getError
获取会自动展现。
查看源码在线预览
import { Input, Button, Checkbox, Field } from '@alifd/next';
const CheckboxGroup = Checkbox.Group;
const list = [
{
value: 'apple',
label: 'apple'
}, {
value: 'pear',
label: 'pear'
}, {
value: 'orange',
label: 'orange'
}
];
class App extends React.Component {
state = {
checkboxStatus: true
}
field = new Field(this, {scrollToFirstError: -10});
isChecked(rule, value, callback) {
if (!value) {
return callback('consent agreement not checked ');
} else {
return callback();
}
}
userName(rule, value, callback) {
if (value === 'frank') {
setTimeout(() => callback('name existed'), 200);
} else {
setTimeout(() => callback(), 200);
}
}
render() {
const init = this.field.init;
return (<div className="demo">
<Input {...init('input', {initValue: 'delete all', rules: {required: true}})} />
{this.field.getError('input') ?
<span style={{color: 'red'}}>{this.field.getError('input').join(',')}</span> : ''}
<br/>
<br/>
<Input placeholder="try onBlur" {...init('input1', {
rules: [{
required: true,
message: 'can not be empty',
trigger: ['onBlur', 'onChange']
}]
})} />
{this.field.getError('input1') ?
<span style={{color: 'red'}}>{this.field.getError('input1').join(',')}</span> : ''}
<br/>
<br/>
<Input defaultValue="" placeholder="try frank" {...init('username', {
rules: [{
validator: this.userName,
trigger: ['onBlur', 'onChange']
}]
})} />
{this.field.getState('username') === 'loading' ? 'validating...' : ''}
{this.field.getError('username') ?
<span style={{color: 'red'}}>{this.field.getError('username').join(',')}</span> : ''}
<br/>
<br/>
agreement:
<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.TextArea placeholder=">3 and <10" {...init('textarea', {
rules: [{
required: true,
minLength: 3,
maxLength: 10
}]
})} />
{this.field.getError('textarea') ?
<span style={{color: 'red'}}>{this.field.getError('textarea').join(',')}</span> : ''}
<br/>
<br/>
{this.state.checkboxStatus ? <div>
Array validate:
<CheckboxGroup dataSource={list} {...init('checkboxgroup', {
rules: [{
required: true,
type: 'array',
message: 'choose one please'
}]
})} 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 ? 'delete' : 'restore'}</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
.demo .next-btn {
margin-right: 5px;
}
在 redux 中使用, 在 componentWillReceiveProps
更新
查看源码在线预览
import { Input, Button, Field } from '@alifd/next';
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 {
componentWillReceiveProps(nextProps) {
this.field.setValues({
email: nextProps.email,
newlen: nextProps.email.length
});
}
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'
}
});
}
render() {
const init = this.field.init;
const newLen = init('newlen', { initValue: this.props.email.length });
return (<div>
<Input {...init('email', { initValue: this.props.email }, {
rules: [
{required: true, type: 'email', message: 'at least 5 chars'}
]
})} />
now length is:{newLen.value}
<p>email: {this.props.email}</p>
<Button onClick={this.setEmail.bind(this)}>set</Button>
</div>);
}
}
const ReduxDemo = connect((state) => {
return {
email: state.formReducer.email
};
})(Demo);
const store = createStore(combineReducers({
formReducer
}));
ReactDOM.render((<Provider store={store}>
<div>
<ReduxDemo />
</div>
</Provider>), mountNode);
autoUnmount 默认为 true,当组件被 unmount 的时候会自动删除数据. autoUnmount 设置为 false 后,会一直保存数据.
查看源码在线预览
import { Input, Button, Field } from '@alifd/next';
class Demo extends React.Component {
state = {
show: true,
show2: true
}
field = new Field(this);
field2 = new Field(this, { autoUnmount: false });
render() {
return (
<div>
{this.state.show ? < Input {...this.field.init('name', { initValue: 'autoUnmount = true' })} /> : null}
<Button
onClick={() => {
console.log('value auto delete', this.field.getValues());
}}
style={{marginLeft: 4}}
>
</Button>
<Button
onClick={() => this.setState({ show: false })}
warning
style={{marginLeft: 4}}
>
delete
</Button>
<br />
<br />
{this.state.show2 ? < Input {...this.field2.init('name2', { initValue: 'autoUnmount = false' })} /> : null}
<Button
onClick={() => {
console.log('value always exist', this.field2.getValues());
}}
style={{marginLeft: 4}}
>
</Button>
<Button
onClick={() => this.setState({ show2: false })}
warning
style={{marginLeft: 4}}
>
delete
</Button>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
通过 spliceArray
可以删除数组格式 name (eg: name.{index}) 的数据, 并且自动订正其他 name 的 index - 1 问题
查看源码在线预览
import { Button, Input, Table, Field } from '@alifd/next';
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
tableSource: [1, 2, 3].map(i => {
return { id: i };
})
};
this.idx = 3;
this.field = new Field(this, {
parseName: true,
});
}
getValues = () => {
const values = this.field.getValues();
console.log(values);
}
add = () => {
const { tableSource } = this.state;
tableSource.push({
id: ++this.idx
});
this.setState({ tableSource });
}
removeItem(index) {
const { tableSource } = this.state;
tableSource.splice(index, 1);
this.field.spliceArray('name.{index}', index);
this.setState({ tableSource });
}
input = (value, index) => <Input {...this.field.init(`name.${index}`, { initValue: index })} />;
delete = (value, index) => <Button warning onClick={this.removeItem.bind(this, index)}>delete</Button>;
render() {
return (
<div>
<Table dataSource={this.state.tableSource}>
<Table.Column title="id" dataIndex="id" />
<Table.Column title="input" dataIndex="id" cell={this.input} />
<Table.Column title="operation" cell={this.delete} width={100} />
</Table>
<div style={{ marginTop: 10 }}>
<Button type="primary" onClick={this.getValues} >print</Button>
<Button type="normal" onClick={this.add} style={{ marginLeft: 8 }}>Add</Button>
</div>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
多组件混合使用
查看源码在线预览
import { Button, Checkbox, Input, Radio, Select, Range, DatePicker, TimePicker, Field } from '@alifd/next';
const CheckboxGroup = Checkbox.Group;
const RadioGroup = Radio.Group;
const list = [
{
value: 'apple',
label: 'apple'
}, {
value: 'pear',
label: 'pear'
}, {
value: 'orange',
label: 'orange'
}
];
const layout = {
marginBottom: 10,
width: 400
};
class App extends React.Component {
field = new Field(this);
render() {
const {init, getValue} = this.field;
return (<div className="demo">
<div style={{marginBottom: 10}}>
<RadioGroup {...init('radiogroup', {initValue: 'a'})} >
<Radio value="a">A</Radio>
<Radio value="b">B</Radio>
<Radio value="c">C</Radio>
<Radio value="d">D</Radio>
</RadioGroup>
</div>
{
getValue('radiogroup') !== 'd' ?
<Select {...init('name', {initValue: 'lucy'})} style={layout}>
<Select.Option value="jack">jack</Select.Option>
<Select.Option value="lucy">lucy</Select.Option>
<Select.Option value="disabled" disabled>disabled</Select.Option>
<Select.Option value="hugohua">hugohua</Select.Option>
</Select> :
<Input {...init('name', {initValue: 'frankqian'})} />
}
<br/>
<Range style={{...layout, marginTop: 30}} slider={'double'} scales={10}
marks={10} {...init('range', {initValue: [20, 40]})}/>
<div style={{marginBottom: 10}}>
<CheckboxGroup dataSource={list} {...init('checkboxgroup', {initValue: ['apple']})} />
</div>
<div style={{marginBottom: 10}}>
<DatePicker {...init('datepicker')}/>
</div>
<div style={{marginBottom: 10}}>
<DatePicker.RangePicker {...init('rangepicker')}/>
</div>
<div style={{marginBottom: 10}}>
<TimePicker {...init('timepicker')}/>
</div>
<Button type="primary" onClick={() => {
console.log(this.field.getValues());
}}>getValues</Button>
<Button onClick={() => {
this.field.setValues({
name: 'hugohua',
range: [30, 50],
checkboxgroup: ['orange'],
radiogroup: 'd'
});
}}>setValues</Button>
<Button onClick={() => {
this.field.reset();
}}>reset</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
.demo .next-btn {
margin-right: 5px;
}
valueName 的默认值为 value,如果为其他需要用 valueName 指定
查看源码在线预览
import { Button, Checkbox, Radio, Switch, Field } from '@alifd/next';
class App extends React.Component {
field = new Field(this);
render() {
const init = this.field.init;
return (<div className="demo">
<Radio {...init('radio', {initValue: false, valueName: 'checked'})} > checked</Radio>
<br/>
<Checkbox {...init('checkbox', {valueName: 'checked', initValue: true})} >
defaultChecked
</Checkbox>
<br/>
<Switch {...init('switch', {valueName: 'checked', initValue: false})}
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);
.demo .next-btn {
margin-right: 5px;
}
自己的组件如何接入Field。
最低标准
: 组件支持 onChange
读取组件数据。达到效果
:Field 可以 getValue,但是 setValue 无效
完全支持
: 组件支持受控, 也就是支持两个api:value
onChange
. value: 设置组件的数据; onChange: 在组件修改的时候在第一个数暴露数据
查看源码在线预览
import { Button, Field } from '@alifd/next';
class Custom extends React.Component {
constructor(props) {
super(props);
this.state = {
value: typeof props.value === 'undefined' ? [] : props.value
};
}
// update value
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
this.setState({
value: typeof nextProps.value === 'undefined' ? [] : nextProps.value
});
}
}
onAdd = () => {
const value = this.state.value.concat([]);
value.push('new');
this.setState({
value
});
this.props.onChange(value);
}
render() {
return (<div className="custom">
{this.state.value.map((v, i) => {
return <Button key={i} >{v}</Button>;
})}
<Button type="primary" onClick={this.onAdd.bind(this)}>Add + </Button>
</div>);
}
}
/* eslint-disable react/no-multi-comp */
class App extends React.Component {
field = new Field(this, {
deepReset: true
});
onGetValue() {
console.log(this.field.getValue('custom'));
}
render() {
const { init, setValue, reset } = this.field;
return (<div className="demo">
<Custom {...init('custom', {initValue: ['test']})} />
<br/><br/>
<Button type="primary" onClick={this.onGetValue.bind(this)}>getValue</Button>
<Button type="primary" onClick={() => setValue('custom', ['test', 'setValue'])}>setValue</Button>
<Button onClick={() => reset()}>reset</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
.demo .next-btn {
margin-right: 5px;
}
.custom {
border: 1px dashed;
padding: 4px;
display: inline-block;
}
.custom span {
border: 1px solid green;
padding: 0px 5px;
height: 24px;
display: inline-block;
margin-right: 2px;
}
把 init('obj.b')
的数据转换成 obj={obj:{b:'value'}}
;
把 init('arr.0')
的数据转换成 obj={arr:['']}
;
查看源码在线预览
import { Input, Button, Field } from '@alifd/next';
class App extends React.Component {
field = new Field(this, {
parseName: true,
values: {
objWithDefaults: {
a: 1,
b: 2
}
}
});
onGetValue() {
console.log(this.field.getValues());
}
onSetValue() {
this.field.setValues({
obj: {
b: 'b',
c: 'c'
},
arr: ['first', 'second']
});
}
render() {
const { init, reset, resetToDefault } = this.field;
return (<div className="demo">
<h3>Object transfer</h3>
obj.b: <Input {...init('obj.b', {initValue: 'test1'})} />
obj.c: <Input {...init('obj.c', {initValue: 'test2'})} />
<br/>
<h3>Array transfer</h3>
arr.0: <Input {...init('arr.0', {initValue: '0'})} />
arr.1: <Input {...init('arr.1', {initValue: '1'})} />
<br/><br/>
<h3>Object with Defaults</h3>
objWithDefaults.a: <Input {...init('objWithDefaults.a')} />
objWithDefaults.b: <Input {...init('objWithDefaults.b')} />
<br/><br/>
result:
<pre>{JSON.stringify(this.field.getValues(), null, 2)}</pre>
<br/><br/>
<Button type="primary" onClick={this.onGetValue.bind(this)}>getValues</Button>
<Button onClick={this.onSetValue.bind(this)}>setValues</Button>
<Button onClick={() => reset()}>reset</Button>
<Button onClick={() => resetToDefault()}>resetToDefault</Button>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
.demo .next-btn {
margin-right: 5px;
}