Field 表单辅助工具

如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令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 值

基本使用

  1. class Demo extends React.Component {
  2. field = new Field(this); // 实例创建
  3. onClick = ()=>{
  4. console.log(this.field.getValue('name'));
  5. }
  6. render() {
  7. const init = this.field.init;
  8. // 注意:initValue只会在组件第一次初始化的时候被赋值,如果你是异步赋值请用setValue
  9. return <div>
  10. <Input {...init('name',{initValue:'first value'})} />
  11. <button onClick={this.onClick>获取数据</button>
  12. </div>
  13. }
  14. }

更新数据

事件更新

  1. class Demo extends React.Component {
  2. field = new Field(this);
  3. onClick = ()=>{
  4. this.field.setValue('name', 'newvalue'); // 赋值会自动触发render
  5. }
  6. render() {
  7. const init = this.field.init;
  8. return <div>
  9. <Input {...init('name')} />
  10. <button onClick={this.onClick}>设置数据</button>
  11. </div>
  12. }
  13. }

props更新

  1. class Demo extends React.Component {
  2. field = new Field(this);
  3. // 在组件挂载之前把数据设置进去(可以用initValue替代这种用法)
  4. componentWillMount() {
  5. this.field.setValue('name', 'init Name')
  6. }
  7. // 接收来自props的数据
  8. componentWillReceiveProps(nextProps) {
  9. this.field.setValue('name', nextProps.name)
  10. }
  11. render() {
  12. const init = this.field.init;
  13. return <div>
  14. <Input {...init('name')} />
  15. </div>
  16. }
  17. }

ajax更新

  1. class Demo extends React.Component {
  2. field = new Field(this);
  3. onClick = ()=>{
  4. Ajax({
  5. url:'/demo.json',
  6. success:(json)=>{
  7. // 回调事件中赋值更新
  8. this.field.setValue('name',json.name);
  9. }
  10. });
  11. }
  12. render() {
  13. const init = this.field.init;
  14. return <div>
  15. <Input {...init('name')} />
  16. <button onClick={this.onClick}>设置数据</button>
  17. </div>
  18. }
  19. }

onChange更新监控

两种用法:1. 统一管理

  1. class Demo extends React.Component {
  2. field = new Field(this,{
  3. onChange:(name, value) => {
  4. switch(name) {
  5. case 'name1':
  6. this.field.setValue('name2','value set by name1');
  7. break;
  8. case 'name2':
  9. this.field.setValue('name1','value set by name2');
  10. break;
  11. }
  12. }
  13. });
  14. render() {
  15. const init = this.field.init;
  16. return <div>
  17. <Input {...init('name1')} />
  18. <Input {...init('name2')} />
  19. </div>
  20. }
  21. }
  • 各自管理
  1. class Demo extends React.Component {
  2. render() {
  3. const init = this.field.init;
  4. return <div>
  5. <Input {...init('name1',{
  6. props:{
  7. onChange:(v)=>{
  8. this.field.setValue('name2','value set by name1');
  9. }
  10. }
  11. })} />
  12. <Input {...init('name2',{
  13. props:{
  14. onChange:(v)=>{
  15. this.field.setValue('name1','value set by name2');
  16. }
  17. }
  18. })} />
  19. </div>
  20. }
  21. }

详细请查看demo演示 关联控制

API

初始化

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

options 配置项

参数说明类型默认值
onChange所有组件的change都会到达这里setValue不会触发该函数Function(name,value)
parseName是否翻译init(name)中的name(getValues会把带.的字符串转换成对象)Booleanfalse
forceUpdate仅建议PureComponent的组件打开此强制刷新功能,会带来性能问题(500个组件为例:打开的时候render花费700ms, 关闭时候render花费400ms)Booleanfalse
scrollToFirstErrorfield.validate的时候滚动到第一个出错的组件, 如果是整数会进行偏移Boolean/Numbertrue
autoUnmount自动删除Unmout元素,如果想保留数据可以设置为falseBooleantrue
autoValidate是否修改数据的时候就自动触发校验, 设为 false 后只能通过 validate() 来触发校验Booleantrue
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校验并获取一组输入域的值与 ErrorFunction([names: String[]], options: Object, callback: Function(errors, values))
getError获取单个输入控件的 ErrorFunction(name: String)
getErrors获取一组输入控件的 ErrorFunction(name: String)
setError设置单个输入控件的 ErrorFunction(name: String, errors:String/ArrayString)
setErrors设置一组输入控件的 ErrorFunction(obj: Object)
reset重置一组输入控件的值、清空校验Function([names: String[]])
resetToDefault重置一组输入控件的值为默认值Function([names: String[]])
getState判断校验状态Function(name: String)'error' 'success' 'loading' ''''
getNames获取所有组件的keyFunction()
remove删除某一个或者一组控件的数据,删除后与之相关的validate/value都会被清空Function(name: String/String[])
spliceArray删除 name 是数组格式的数据, 并且自动处理其他 name 的数组错位问题Function(keyMatch: String, index: Number)

init

  1. init(name, options, props)
参数说明类型可选值默认值
name必填输入控件唯一标志String
options.valueName组件值的属性名称,如 Checkbox 的是 checked,Input是 valueString'value'
options.initValue组件初始值(组件第一次render的时候才会读取,后面再修改此值无效),类似defaultValueany
options.trigger触发数据变化的事件名称String'onChange'
options.rules校验规则Array/Object
options.getValueFromEvent自定义从onChange事件中获取value的方式,一般不需要设置. 详细用法查看demo 自定义数据获取Function(value,…args) 参数顺序和组件是完全一致的
props组件自定义的事件可以写在这里Object
autoValidate是否修改数据的时候自动触发校验单个组件的校验, 设为 false 后只能通过 validate() 来触发校验Booleantrue

返回值

  1. {id,value,onChange}

rules

  1. {
  2. rules:[{ required: true }]
  3. }

多个rule

  1. {
  2. rules:[{required:true,trigger:'onBlur'},{pattern:/abcd/,message:'abcd不能缺'},{validator:(rule, value, callback)=>{callback('出错了')}}]
  3. }
参数说明类型可选值使用类型
required不能为空Booleantrueundefined/null/“”/[] 会触发此规则)
pattern校验正则表达式正则
minLength字符串最小长度 / 数组最小个数NumberString/Number/Array
maxLength字符串最大长度 / 数组最大个数NumberString/Number/Array
length字符串精确长度 / 数组精确个数NumberString/Number/Array
min最小值NumberString/Number
max最大值NumberString/Number
format对常用 pattern 的总结Stringurl、email、tel、numberString
validator自定义校验,(校验成功的时候不要忘记执行 callback(),否则会校验不返回)Function(rule,value,callback)
trigger触发校验的事件名称String/ArrayonChange/onBlur/…onChange
message出错时候信息String

自定义组件接入Field标准

  • 支持受控模式(value+onChange) 必须

    • value 控制组件数据展现

    • onChange 组件发生变化时候的回调函数(第一个参数可以给到value)

  • 一次完整操作抛一次onChange事件 建议 比如有Process表示进展中的状态,建议增加API onProcess;如果有Start表示启动状态,建议增加API onStart

  • value={undefined}的时候清空数据, field 的 reset 函数会给所有组件下发 undefined 数据 建议

  1. componentWillReceiveProps(nextProps) {
  2. if ('value' in nextProps ) {
  3. this.setState({
  4. value: nextProps.value === undefined? []: nextProps.value // 设置组件的被清空后的数值
  5. })
  6. }
  7. }

已知问题

  • 为何手动调用this.field.validate的时候进不了回调函数? 答: 是不是自定义了validator方法,确保callback在任何分支下都能被执行到。

代码示例

基本

getValue setValue reset 的使用

Field 表单辅助工具 - 图1

查看源码在线预览

  1. import { Input, Button, Field } from '@alifd/next';
  2. class App extends React.Component {
  3. field = new Field(this);
  4. onGetValue() {
  5. console.log(this.field.getValue('input'));
  6. }
  7. render() {
  8. const { init, setValue, reset } = this.field;
  9. return (<div className="demo">
  10. <Input {...init('input', {initValue: 'test'})} />
  11. <br/><br/>
  12. <Button type="primary" onClick={this.onGetValue.bind(this)}>getValue</Button>
  13. <Button type="primary" onClick={() => setValue('input', 'set me by click')}>setValue</Button>
  14. <Button onClick={() => reset()}>reset</Button>
  15. </div>);
  16. }
  17. }
  18. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }

关联控制

组件之间的关联控制. onChange 统一管理。

Field 表单辅助工具 - 图2

查看源码在线预览

  1. import { Input, Select, Range, Field } from '@alifd/next';
  2. class App extends React.Component {
  3. field = new Field(this, {
  4. onChange: (name, value) => {
  5. console.log(this.field.getValues());
  6. switch (name) {
  7. case 'input':
  8. this.field.setValue('sync', `change to: ${value}`);
  9. break;
  10. case 'select':
  11. this.field.setValue('sync', `${value} is coming`);
  12. break;
  13. case 'range':
  14. this.field.setValue('sync', ` (${value.join(',')}) ready`);
  15. break;
  16. }
  17. }
  18. });
  19. render() {
  20. const {init, getValue} = this.field;
  21. const layout = {
  22. marginBottom: 10,
  23. width: 400
  24. };
  25. return (<div>
  26. <Input placeholder="controlled by onChange" {...init('input')} style={layout}/><br/>
  27. <Input placeholder="under control" {...init('input')} style={layout}/><br/>
  28. <Select style={layout} {...init('select', {initValue: 'lucy'})}>
  29. <Select.Option value="jack">jack</Select.Option>
  30. <Select.Option value="lucy">lucy</Select.Option>
  31. <Select.Option value="disabled" disabled>disabled</Select.Option>
  32. <Select.Option value="hugo">hugo</Select.Option>
  33. </Select><br/>
  34. {
  35. getValue('select') !== 'hugo' ?
  36. <Range
  37. style={{...layout, marginTop: 30}}
  38. slider={'double'} scales={10} marks={10}
  39. {...init('range', {initValue: [20, 40], trigger: 'onProcess'})}
  40. /> : null
  41. }
  42. <br/>
  43. <hr style={{marginBottom: 10}}/>
  44. <Input placeholder="everyone can control me" {...init('sync')} style={layout}/><br/>
  45. </div>);
  46. }
  47. }
  48. ReactDOM.render(<App/>, mountNode);

自定义返回值

通过 getValueFromEvent 自定义从组件的 Event 获取 value 的方式

Field 表单辅助工具 - 图3

查看源码在线预览

  1. import { Button, Input, Field } from '@alifd/next';
  2. class App extends React.Component {
  3. field = new Field(this);
  4. normFile(list) {
  5. if (Array.isArray(list)) {
  6. return list;
  7. }
  8. return list && list.fileList;
  9. }
  10. normDate(date, strdate) {
  11. console.log('normDate:', date, strdate);
  12. return strdate;
  13. }
  14. render() {
  15. const init = this.field.init;
  16. return (<div>
  17. <Input {...init('name', { getValueFromEvent: (value) => {
  18. if (value.match(/##/)) {
  19. return value;
  20. } else {
  21. return `## title-${value}`;
  22. }
  23. }})} />
  24. <Button type="primary" onClick={() => {
  25. console.log(this.field.getValues());
  26. }}>getValues</Button>
  27. </div>);
  28. }
  29. }
  30. ReactDOM.render(<App/>, mountNode);

自定义错误

自己控制组件的errors

Field 表单辅助工具 - 图4

查看源码在线预览

  1. import { Input, Button, Field } from '@alifd/next';
  2. class App extends React.Component {
  3. field = new Field(this);
  4. validate = () => {
  5. console.log(this.field.getErrors());
  6. this.field.validate((error, values) => {
  7. // eslint-disable-next-line no-alert
  8. alert(JSON.stringify(error));
  9. });
  10. };
  11. render() {
  12. const { init, getError, setError, setErrors } = this.field;
  13. return (<div className="demo">
  14. <Input {...init('input', {
  15. rules: [{
  16. required: true,
  17. pattern: /hello/,
  18. message: 'must be hello'
  19. }]
  20. })} /><br/>
  21. <span style={{color: 'red'}}>{getError('input')}</span>
  22. <br/>
  23. <Button onClick={() => {
  24. setError('input', 'set error 1');
  25. }}>setError</Button>
  26. <Button onClick={() => {
  27. setErrors({input: 'set error 2'});
  28. }}>setErrors</Button>
  29. <Button onClick={() => {
  30. setErrors({input: ''});
  31. }}>clear</Button>
  32. <br/><br/>
  33. <Input {...init('input2')} /><br/>
  34. <span style={{color: 'red'}}>{getError('input2')}</span><br/>
  35. <Button onClick={() => {
  36. setError('input2', 'errors will be removed by onChange and shown on validate');
  37. }}>setError</Button>
  38. <Button onClick={this.validate}>
  39. validate</Button>
  40. </div>);
  41. }
  42. }
  43. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }

校验

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

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

Field 表单辅助工具 - 图5

查看源码在线预览

  1. import { Input, Button, Checkbox, Field } from '@alifd/next';
  2. const CheckboxGroup = Checkbox.Group;
  3. const list = [
  4. {
  5. value: 'apple',
  6. label: 'apple'
  7. }, {
  8. value: 'pear',
  9. label: 'pear'
  10. }, {
  11. value: 'orange',
  12. label: 'orange'
  13. }
  14. ];
  15. class App extends React.Component {
  16. state = {
  17. checkboxStatus: true
  18. }
  19. field = new Field(this, {scrollToFirstError: -10});
  20. isChecked(rule, value, callback) {
  21. if (!value) {
  22. return callback('consent agreement not checked ');
  23. } else {
  24. return callback();
  25. }
  26. }
  27. userName(rule, value, callback) {
  28. if (value === 'frank') {
  29. setTimeout(() => callback('name existed'), 200);
  30. } else {
  31. setTimeout(() => callback(), 200);
  32. }
  33. }
  34. render() {
  35. const init = this.field.init;
  36. return (<div className="demo">
  37. <Input {...init('input', {initValue: 'delete all', rules: {required: true}})} />
  38. {this.field.getError('input') ?
  39. <span style={{color: 'red'}}>{this.field.getError('input').join(',')}</span> : ''}
  40. <br/>
  41. <br/>
  42. <Input placeholder="try onBlur" {...init('input1', {
  43. rules: [{
  44. required: true,
  45. message: 'can not be empty',
  46. trigger: ['onBlur', 'onChange']
  47. }]
  48. })} />
  49. {this.field.getError('input1') ?
  50. <span style={{color: 'red'}}>{this.field.getError('input1').join(',')}</span> : ''}
  51. <br/>
  52. <br/>
  53. <Input defaultValue="" placeholder="try frank" {...init('username', {
  54. rules: [{
  55. validator: this.userName,
  56. trigger: ['onBlur', 'onChange']
  57. }]
  58. })} />
  59. {this.field.getState('username') === 'loading' ? 'validating...' : ''}
  60. {this.field.getError('username') ?
  61. <span style={{color: 'red'}}>{this.field.getError('username').join(',')}</span> : ''}
  62. <br/>
  63. <br/>
  64. agreement:
  65. <Checkbox {...init('checkbox', {
  66. valueName: 'checked',
  67. rules: [{validator: this.isChecked}]
  68. })} />
  69. {this.field.getError('checkbox') ?
  70. <span style={{color: 'red'}}>{this.field.getError('checkbox').join(',')}</span> : ''}
  71. <br/>
  72. <br/>
  73. <Input.TextArea placeholder=">3 and <10" {...init('textarea', {
  74. rules: [{
  75. required: true,
  76. minLength: 3,
  77. maxLength: 10
  78. }]
  79. })} />
  80. {this.field.getError('textarea') ?
  81. <span style={{color: 'red'}}>{this.field.getError('textarea').join(',')}</span> : ''}
  82. <br/>
  83. <br/>
  84. {this.state.checkboxStatus ? <div>
  85. Array validate
  86. <CheckboxGroup dataSource={list} {...init('checkboxgroup', {
  87. rules: [{
  88. required: true,
  89. type: 'array',
  90. message: 'choose one please'
  91. }]
  92. })} style={{marginBottom: 10}}/>
  93. {this.field.getError('checkboxgroup') ?
  94. <span style={{color: 'red'}}>{this.field.getError('checkboxgroup').join(',')}</span> : ''}
  95. </div> : null}
  96. <br/>
  97. <br/>
  98. <Button type="primary" onClick={() => {
  99. this.field.validate((errors, values) => {
  100. console.log(errors, values);
  101. });
  102. }}>validate</Button>
  103. <Button onClick={() => {
  104. this.field.reset();
  105. }}>reset</Button>
  106. <Button onClick={() => {
  107. if (this.state.checkboxStatus) {
  108. this.setState({checkboxStatus: false});
  109. this.field.remove('checkboxgroup');
  110. } else {
  111. this.setState({checkboxStatus: true});
  112. }
  113. }}>{this.state.checkboxStatus ? 'delete' : 'restore'}</Button>
  114. </div>);
  115. }
  116. }
  117. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }

redux 中使用

在 redux 中使用, 在 componentWillReceiveProps 更新

Field 表单辅助工具 - 图6

查看源码在线预览

  1. import { Input, Button, Field } from '@alifd/next';
  2. import { combineReducers, createStore } from 'redux';
  3. import { Provider, connect } from 'react-redux';
  4. function formReducer(state = {email: 'frankqian@qq.com'}, action) {
  5. switch (action.type) {
  6. case 'save_fields':
  7. return {
  8. ...state,
  9. ...action.payload
  10. };
  11. default:
  12. return state;
  13. }
  14. }
  15. class Demo extends React.Component {
  16. componentWillReceiveProps(nextProps) {
  17. this.field.setValues({
  18. email: nextProps.email,
  19. newlen: nextProps.email.length
  20. });
  21. }
  22. field = new Field(this, {
  23. onChange: (name, value) => {
  24. console.log('onChange', name, value);
  25. this.field.setValue('newlen', value.length);
  26. this.props.dispatch({
  27. type: 'save_fields',
  28. payload: {
  29. [name]: value
  30. }
  31. });
  32. }
  33. });
  34. setEmail() {
  35. this.props.dispatch({
  36. type: 'save_fields',
  37. payload: {
  38. email: 'qq@gmail.com'
  39. }
  40. });
  41. }
  42. render() {
  43. const init = this.field.init;
  44. const newLen = init('newlen', { initValue: this.props.email.length });
  45. return (<div>
  46. <Input {...init('email', { initValue: this.props.email }, {
  47. rules: [
  48. {required: true, type: 'email', message: 'at least 5 chars'}
  49. ]
  50. })} />
  51. now length is:{newLen.value}
  52. <p>email: {this.props.email}</p>
  53. <Button onClick={this.setEmail.bind(this)}>set</Button>
  54. </div>);
  55. }
  56. }
  57. const ReduxDemo = connect((state) => {
  58. return {
  59. email: state.formReducer.email
  60. };
  61. })(Demo);
  62. const store = createStore(combineReducers({
  63. formReducer
  64. }));
  65. ReactDOM.render((<Provider store={store}>
  66. <div>
  67. <ReduxDemo />
  68. </div>
  69. </Provider>), mountNode);

自动卸载

autoUnmount 默认为 true,当组件被 unmount 的时候会自动删除数据. autoUnmount 设置为 false 后,会一直保存数据.

Field 表单辅助工具 - 图7

查看源码在线预览

  1. import { Input, Button, Field } from '@alifd/next';
  2. class Demo extends React.Component {
  3. state = {
  4. show: true,
  5. show2: true
  6. }
  7. field = new Field(this);
  8. field2 = new Field(this, { autoUnmount: false });
  9. render() {
  10. return (
  11. <div>
  12. {this.state.show ? < Input {...this.field.init('name', { initValue: 'autoUnmount = true' })} /> : null}
  13. <Button
  14. onClick={() => {
  15. console.log('value auto delete', this.field.getValues());
  16. }}
  17. style={{marginLeft: 4}}
  18. >
  19. print
  20. </Button>
  21. <Button
  22. onClick={() => this.setState({ show: false })}
  23. warning
  24. style={{marginLeft: 4}}
  25. >
  26. delete
  27. </Button>
  28. <br />
  29. <br />
  30. {this.state.show2 ? < Input {...this.field2.init('name2', { initValue: 'autoUnmount = false' })} /> : null}
  31. <Button
  32. onClick={() => {
  33. console.log('value always exist', this.field2.getValues());
  34. }}
  35. style={{marginLeft: 4}}
  36. >
  37. print
  38. </Button>
  39. <Button
  40. onClick={() => this.setState({ show2: false })}
  41. warning
  42. style={{marginLeft: 4}}
  43. >
  44. delete
  45. </Button>
  46. </div>
  47. );
  48. }
  49. }
  50. ReactDOM.render(<Demo />, mountNode);

动态表格

通过 spliceArray 可以删除数组格式 name (eg: name.{index}) 的数据, 并且自动订正其他 name 的 index - 1 问题

Field 表单辅助工具 - 图8

查看源码在线预览

  1. import { Button, Input, Table, Field } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. tableSource: [1, 2, 3].map(i => {
  7. return { id: i };
  8. })
  9. };
  10. this.idx = 3;
  11. this.field = new Field(this, {
  12. parseName: true,
  13. });
  14. }
  15. getValues = () => {
  16. const values = this.field.getValues();
  17. console.log(values);
  18. }
  19. add = () => {
  20. const { tableSource } = this.state;
  21. tableSource.push({
  22. id: ++this.idx
  23. });
  24. this.setState({ tableSource });
  25. }
  26. removeItem(index) {
  27. const { tableSource } = this.state;
  28. tableSource.splice(index, 1);
  29. this.field.spliceArray('name.{index}', index);
  30. this.setState({ tableSource });
  31. }
  32. input = (value, index) => <Input {...this.field.init(`name.${index}`, { initValue: index })} />;
  33. delete = (value, index) => <Button warning onClick={this.removeItem.bind(this, index)}>delete</Button>;
  34. render() {
  35. return (
  36. <div>
  37. <Table dataSource={this.state.tableSource}>
  38. <Table.Column title="id" dataIndex="id" />
  39. <Table.Column title="input" dataIndex="id" cell={this.input} />
  40. <Table.Column title="operation" cell={this.delete} width={100} />
  41. </Table>
  42. <div style={{ marginTop: 10 }}>
  43. <Button type="primary" onClick={this.getValues} >print</Button>
  44. <Button type="normal" onClick={this.add} style={{ marginLeft: 8 }}>Add</Button>
  45. </div>
  46. </div>
  47. );
  48. }
  49. }
  50. ReactDOM.render(<Demo />, mountNode);

组合使用

多组件混合使用

Field 表单辅助工具 - 图9

查看源码在线预览

  1. import { Button, Checkbox, Input, Radio, Select, Range, DatePicker, TimePicker, Field } from '@alifd/next';
  2. const CheckboxGroup = Checkbox.Group;
  3. const RadioGroup = Radio.Group;
  4. const list = [
  5. {
  6. value: 'apple',
  7. label: 'apple'
  8. }, {
  9. value: 'pear',
  10. label: 'pear'
  11. }, {
  12. value: 'orange',
  13. label: 'orange'
  14. }
  15. ];
  16. const layout = {
  17. marginBottom: 10,
  18. width: 400
  19. };
  20. class App extends React.Component {
  21. field = new Field(this);
  22. render() {
  23. const {init, getValue} = this.field;
  24. return (<div className="demo">
  25. <div style={{marginBottom: 10}}>
  26. <RadioGroup {...init('radiogroup', {initValue: 'a'})} >
  27. <Radio value="a">A</Radio>
  28. <Radio value="b">B</Radio>
  29. <Radio value="c">C</Radio>
  30. <Radio value="d">D</Radio>
  31. </RadioGroup>
  32. </div>
  33. {
  34. getValue('radiogroup') !== 'd' ?
  35. <Select {...init('name', {initValue: 'lucy'})} style={layout}>
  36. <Select.Option value="jack">jack</Select.Option>
  37. <Select.Option value="lucy">lucy</Select.Option>
  38. <Select.Option value="disabled" disabled>disabled</Select.Option>
  39. <Select.Option value="hugohua">hugohua</Select.Option>
  40. </Select> :
  41. <Input {...init('name', {initValue: 'frankqian'})} />
  42. }
  43. <br/>
  44. <Range style={{...layout, marginTop: 30}} slider={'double'} scales={10}
  45. marks={10} {...init('range', {initValue: [20, 40]})}/>
  46. <div style={{marginBottom: 10}}>
  47. <CheckboxGroup dataSource={list} {...init('checkboxgroup', {initValue: ['apple']})} />
  48. </div>
  49. <div style={{marginBottom: 10}}>
  50. <DatePicker {...init('datepicker')}/>
  51. </div>
  52. <div style={{marginBottom: 10}}>
  53. <DatePicker.RangePicker {...init('rangepicker')}/>
  54. </div>
  55. <div style={{marginBottom: 10}}>
  56. <TimePicker {...init('timepicker')}/>
  57. </div>
  58. <Button type="primary" onClick={() => {
  59. console.log(this.field.getValues());
  60. }}>getValues</Button>
  61. <Button onClick={() => {
  62. this.field.setValues({
  63. name: 'hugohua',
  64. range: [30, 50],
  65. checkboxgroup: ['orange'],
  66. radiogroup: 'd'
  67. });
  68. }}>setValues</Button>
  69. <Button onClick={() => {
  70. this.field.reset();
  71. }}>reset</Button>
  72. </div>);
  73. }
  74. }
  75. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }

自定义受控字段

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

Field 表单辅助工具 - 图10

查看源码在线预览

  1. import { Button, Checkbox, Radio, Switch, Field } from '@alifd/next';
  2. class App extends React.Component {
  3. field = new Field(this);
  4. render() {
  5. const init = this.field.init;
  6. return (<div className="demo">
  7. <Radio {...init('radio', {initValue: false, valueName: 'checked'})} > checked</Radio>
  8. <br/>
  9. <Checkbox {...init('checkbox', {valueName: 'checked', initValue: true})} >
  10. defaultChecked
  11. </Checkbox>
  12. <br/>
  13. <Switch {...init('switch', {valueName: 'checked', initValue: false})}
  14. style={{marginTop: 10, marginBottom: 10}}/>
  15. <br/>
  16. <Button type="primary" onClick={() => {
  17. console.log(this.field.getValues());
  18. }}>getValues</Button>
  19. <Button onClick={() => {
  20. this.field.setValues({
  21. radio: true,
  22. switch: true,
  23. checkbox: false
  24. });
  25. }}> setValues </Button>
  26. <Button onClick={() => {
  27. this.field.reset();
  28. }}>reset</Button>
  29. </div>);
  30. }
  31. }
  32. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }

自定义组件

自己的组件如何接入Field。

最低标准: 组件支持 onChange 读取组件数据。达到效果:Field 可以 getValue,但是 setValue 无效

完全支持: 组件支持受控, 也就是支持两个api:value onChange. value: 设置组件的数据; onChange: 在组件修改的时候在第一个数暴露数据

Field 表单辅助工具 - 图11

查看源码在线预览

  1. import { Button, Field } from '@alifd/next';
  2. class Custom extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. value: typeof props.value === 'undefined' ? [] : props.value
  7. };
  8. }
  9. // update value
  10. componentWillReceiveProps(nextProps) {
  11. if ('value' in nextProps) {
  12. this.setState({
  13. value: typeof nextProps.value === 'undefined' ? [] : nextProps.value
  14. });
  15. }
  16. }
  17. onAdd = () => {
  18. const value = this.state.value.concat([]);
  19. value.push('new');
  20. this.setState({
  21. value
  22. });
  23. this.props.onChange(value);
  24. }
  25. render() {
  26. return (<div className="custom">
  27. {this.state.value.map((v, i) => {
  28. return <Button key={i} >{v}</Button>;
  29. })}
  30. <Button type="primary" onClick={this.onAdd.bind(this)}>Add </Button>
  31. </div>);
  32. }
  33. }
  34. /* eslint-disable react/no-multi-comp */
  35. class App extends React.Component {
  36. field = new Field(this, {
  37. deepReset: true
  38. });
  39. onGetValue() {
  40. console.log(this.field.getValue('custom'));
  41. }
  42. render() {
  43. const { init, setValue, reset } = this.field;
  44. return (<div className="demo">
  45. <Custom {...init('custom', {initValue: ['test']})} />
  46. <br/><br/>
  47. <Button type="primary" onClick={this.onGetValue.bind(this)}>getValue</Button>
  48. <Button type="primary" onClick={() => setValue('custom', ['test', 'setValue'])}>setValue</Button>
  49. <Button onClick={() => reset()}>reset</Button>
  50. </div>);
  51. }
  52. }
  53. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }
  4. .custom {
  5. border: 1px dashed;
  6. padding: 4px;
  7. display: inline-block;
  8. }
  9. .custom span {
  10. border: 1px solid green;
  11. padding: 0px 5px;
  12. height: 24px;
  13. display: inline-block;
  14. margin-right: 2px;
  15. }

结构化解析

init('obj.b') 的数据转换成 obj={obj:{b:'value'}}

init('arr.0') 的数据转换成 obj={arr:['']}

Field 表单辅助工具 - 图12

查看源码在线预览

  1. import { Input, Button, Field } from '@alifd/next';
  2. class App extends React.Component {
  3. field = new Field(this, {
  4. parseName: true,
  5. values: {
  6. objWithDefaults: {
  7. a: 1,
  8. b: 2
  9. }
  10. }
  11. });
  12. onGetValue() {
  13. console.log(this.field.getValues());
  14. }
  15. onSetValue() {
  16. this.field.setValues({
  17. obj: {
  18. b: 'b',
  19. c: 'c'
  20. },
  21. arr: ['first', 'second']
  22. });
  23. }
  24. render() {
  25. const { init, reset, resetToDefault } = this.field;
  26. return (<div className="demo">
  27. <h3>Object transfer</h3>
  28. obj.b: <Input {...init('obj.b', {initValue: 'test1'})} /> &nbsp;
  29. obj.c: <Input {...init('obj.c', {initValue: 'test2'})} />
  30. <br/>
  31. <h3>Array transfer</h3>
  32. arr.0: <Input {...init('arr.0', {initValue: '0'})} /> &nbsp;
  33. arr.1: <Input {...init('arr.1', {initValue: '1'})} />
  34. <br/><br/>
  35. <h3>Object with Defaults</h3>
  36. objWithDefaults.a: <Input {...init('objWithDefaults.a')} /> &nbsp;
  37. objWithDefaults.b: <Input {...init('objWithDefaults.b')} />
  38. <br/><br/>
  39. result:
  40. <pre>{JSON.stringify(this.field.getValues(), null, 2)}</pre>
  41. <br/><br/>
  42. <Button type="primary" onClick={this.onGetValue.bind(this)}>getValues</Button>
  43. <Button onClick={this.onSetValue.bind(this)}>setValues</Button>
  44. <Button onClick={() => reset()}>reset</Button>
  45. <Button onClick={() => resetToDefault()}>resetToDefault</Button>
  46. </div>);
  47. }
  48. }
  49. ReactDOM.render(<App/>, mountNode);
  1. .demo .next-btn {
  2. margin-right: 5px;
  3. }

相关区块

Field 表单辅助工具 - 图13

暂无相关区块