ICE 表单数据获取方案。
说明:
npm install @icedesign/form-binder
表单元素指的是 ICE 基础组件以及业务组件中的 Input
、 Checkbox
、 Select
、 Range
、 DatePicker
、 TimePicker
、 NumberPicker
、 Switch
、 Upload
等以及用户自定义的组件,它能够响应 onChange
等用来获取用户输入
方法名 | 说明 | 类型 | 默认值 |
---|---|---|---|
validateFields | 校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件 | ([fieldNames: string[]],callback(errors,values)) => void |
整个表单的容器,支持传入 value 和 onChange 等属性,其中 value 会作为整个表单数据根节点,交由下层组件去获取、更新操作
经过 FormBinderWrapper 包装的组件,数据传递和同步将被接管,这意味着:
onChange
来做同步,但还是可以继续监听 onChange
等事件value
、 defaultValue
等属性来设置表单元素的值,但可以通过初始的 value 进行设置属性参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
value | 表单值 | object | {} |
onChange | 任一表单域的值发生改变时的回调 | function(value) | () => {} |
enableScrollErrorField | 全局校验时,是否开启滚动到报错表单位置 | boolean | false |
scrollErrorFieldTopOffset | 全局校验滚动到报错位置时,距离顶部的偏移值(适用于头部 fixed 的场景) | number | 0 |
表单组件粘合剂,将其作为 FormBinderWrapper 的子组件,即可实现双向绑定特性,之后表单域的改变会通过 FormBinder 转发从而响应到 FormBinderWrapper 的 onChange 方法进行通信
FormBinder 支持的属性包含以下两部分:
自定义规则
属性参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
rules | 校验规则,参考下方文档 | object[] | |
name | 表单域名称 | string | |
setFieldValue | 设置一个输入控件的值 | Function(fieldName: string) | |
getFieldValue | 获取一个输入控件的值 | Function(fieldName: string) | |
triggerType | 指定合适的触发事件 | string | 'onChange' |
校验规则
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
enum | 枚举类型 | string | |
len | 字段长度 | number | |
max | 最大长度 | number | |
message | 校验文案 | string | |
min | 最小长度 | number | |
pattern | 正则表达式校验 | RegExp | |
required | 是否必选 | boolean | false |
transform | 校验前转换字段值 | function(value) => transformedValue:any | |
type | 内建校验类型 | string | 'string' |
validator | 自定义校验 | function(rule, value, callback) | |
whitespace | 必选时,空格是否会被视为错误 | boolean | false |
内建校验类型,可选项 自定义校验(注意,callback 必须被调用)
推荐:
自定义表单的报错信息,自定义报错信息时需要指定 name,以此来获取当前报错的表单域来源
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
name | 表单域名称 | string | |
style | 自定义样式对象 | object | |
className | 自定义样式类名 | string | |
render | 自定义渲染报错的组件和处理逻辑 | Function(errors):ReactNode |
双向绑定协议指的是组件接收 value
和 onChange
两个参数,其用户输入值由 value 提供,当用户操作组件导致数据变更,组件会调用 onChange
并把新的 value
作为第一个参数传出。React 社区的大多数组件都遵守这个设计,如 @alifd/next
的 Input
, Select
, Checkbox
等,如果你希望你的表单类组件能够接入 FormBinder ,请务必遵守这个协议。
普通的登录表单,可以自由组合布局,自定义排列标签和表单域
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Input, Button, Checkbox, Message } from '@alifd/next';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
value: {
username: '',
password: '',
checkbox: false
}
};
}
formChange = value => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value });
};
validateFields = () => {
const { validateFields } = this.refs.form;
validateFields((errors, values) => {
console.log({ errors })
if (!errors) {
Message.success('登录成功')
}
});
}
render() {
return (
<div style={styles.container}>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
ref="form"
>
<div style={styles.content}>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>名称:</span>
<FormBinder name="username" required message="请输入正确的名称" >
<Input />
</FormBinder>
<FormError style={styles.formItemError} name="username" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>密码:</span>
<FormBinder name="password" required message="请输入正确的密码">
<Input htmlType="password" />
</FormBinder>
<FormError style={styles.formItemError} name="password" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>记住密码:</span>
<FormBinder
name="checkbox"
>
<Checkbox />
</FormBinder>
</div>
<Button type="primary" style={{width: '242px'}} onClick={this.validateFields}>
登 录
</Button>
</div>
</FormBinderWrapper>
<div style={styles.preview}>
<strong>当前表单数据</strong>
<pre>{JSON.stringify(this.state.value, null, 2)}</pre>
</div>
</div>
);
}
}
const styles = {
formItem: {
marginBottom: '20px'
},
formItemLabel: {
},
formItemError: {
marginLeft: '10px',
},
preview: {
border: '1px solid #eee',
marginTop: 20,
padding: 10
}
}
ReactDOM.render(<Login />, mountNode);
普通的注册表单,展示多表单元素的组合,用户填写必须的信息才能注册新用户。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Input, Button, Checkbox, Message, CascaderSelect, Switch, DatePicker } from '@alifd/next';
const cityData = [
{
value: "2973",
label: "陕西",
children: [
{
value: "2974",
label: "西安",
children: [
{ value: "2975", label: "西安市" },
{ value: "2976", label: "高陵县" }
]
},
{
value: "2980",
label: "铜川",
children: [
{ value: "2981", label: "铜川市" },
{ value: "2982", label: "宜君县" }
]
}
]
},
{
value: "3371",
label: "新疆",
children: [
{
value: "3430",
label: "巴音郭楞蒙古自治州",
children: [
{ value: "3431", label: "库尔勒市" },
{ value: "3432", label: "和静县" }
]
}
]
}
];
class Register extends Component {
constructor(props) {
super(props);
this.state = {
value: {
email: '',
password: '',
confirmPassword: '',
nickname: '',
birthdate: '',
city: '',
notification: false,
agreement: false,
}
};
}
formChange = value => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value });
};
checkPassword = (rule, values, callback) => {
if (!values) {
callback('请输入正确的密码');
} else if (values.length < 8) {
callback('密码必须大于8位');
} else if (values.length > 16) {
callback('密码必须小于16位');
} else {
callback();
}
};
checkConfirmPassword = (rule, values, callback) => {
if (!values) {
callback('请输入正确的密码');
} else if (values && values !== this.state.value.password) {
callback('两次输入密码不一致');
} else {
callback();
}
};
validateFields = () => {
const { validateFields } = this.refs.form;
validateFields((errors, values) => {
console.log({ errors })
if (!errors) {
Message.success('注册成功')
}
});
}
render() {
return (
<div style={styles.container}>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
ref="form"
>
<div style={styles.content}>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>邮箱:</span>
<FormBinder name="email" required type="email" message="请输入正确的邮箱" >
<Input placeholder="ice-admin@alibaba-inc.com" />
</FormBinder>
<FormError style={styles.formItemError} name="email" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>昵称:</span>
<FormBinder name="nickname" required message="请输入正确的昵称" >
<Input placeholder="淘小宝" />
</FormBinder>
<FormError style={styles.formItemError} name="nickname" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>密码:</span>
<FormBinder name="password" required validator={this.checkPassword}>
<Input htmlType="password" placeholder="输入密码" />
</FormBinder>
<FormError style={styles.formItemError} name="password" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>确认密码:</span>
<FormBinder name="confirmPassword" required validator={this.checkConfirmPassword}>
<Input htmlType="password" placeholder="输入确认密码" />
</FormBinder>
<FormError style={styles.formItemError} name="confirmPassword" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>出生日期:</span>
<FormBinder name="birthdate" getFieldValue={(date, formatDate) => { return formatDate }}>
<DatePicker format="YYYY-MM-DD" style={{ width: '200px' }} />
</FormBinder>
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>籍贯地址:</span>
<FormBinder name="city" >
<CascaderSelect dataSource={cityData} style={{ width: '200px' }} />
</FormBinder>
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>开启通知:</span>
<FormBinder name="notification" valuePropName="checked">
<Switch />
</FormBinder>
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>用户协议:</span>
<FormBinder
name="agreement"
valuePropName="checked"
>
<Checkbox />
</FormBinder>
</div>
<Button type="primary" style={{width: '270px'}} onClick={this.validateFields}>
注 册
</Button>
</div>
</FormBinderWrapper>
<div style={styles.preview}>
<strong>当前表单数据</strong>
<pre>{JSON.stringify(this.state.value, null, 2)}</pre>
</div>
</div>
);
}
}
const styles = {
formItem: {
marginBottom: '20px',
display: 'flex',
alignItems: 'center',
},
formItemLabel: {
width: '70px',
mariginRight: '10px',
display: 'inline-block',
textAlign: 'right',
},
formItemError: {
marginLeft: '10px',
},
preview: {
border: '1px solid #eee',
marginTop: 20,
padding: 10
}
}
ReactDOM.render(<Register />, mountNode);
时间类组件的 value 类型为 moment 对象,一般在提交服务器前需要预处理
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { DatePicker, TimePicker, Button } from '@alifd/next';
import moment from 'moment';
moment.locale('zh-cn');
const { MonthPicker, RangePicker } = DatePicker;
class Time extends Component {
constructor(props) {
super(props);
this.state = {
value: {
datePicker: '',
dateTimePicker: '',
monthPicker: '',
rangePicker: [],
rangeRimePicker: [],
timePicker: '',
}
};
}
formChange = value => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value });
};
validateFields = () => {
const { validateFields } = this.refs.form;
validateFields((errors, values) => {
console.log(errors, values)
});
}
render() {
const config = {
required: true,
message: "请选择",
}
const style = {
width: '350px',
}
return (
<div style={styles.container}>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
ref="form"
>
<div style={styles.content}>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>日期选择:</span>
<FormBinder name="datePicker" {...config}>
<DatePicker format="YYYY-MM-DD" style={{...style}} />
</FormBinder>
<FormError style={styles.formItemError} name="datePicker" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>日期时间:</span>
<FormBinder name="dateTimePicker" {...config}>
<DatePicker showTime format="YYYY-MM-DD" style={{...style}} />
</FormBinder>
<FormError style={styles.formItemError} name="dateTimePicker" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>月份选择:</span>
<FormBinder name="monthPicker" {...config}>
<MonthPicker format="YYYY-MM" style={{...style}} />
</FormBinder>
<FormError style={styles.formItemError} name="monthPicker" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>区间选择:</span>
<FormBinder name="rangePicker" {...config}>
<RangePicker format="YYYY-MM-DD" style={{...style}} />
</FormBinder>
<FormError style={styles.formItemError} name="rangePicker" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>区间时间:</span>
<FormBinder name="rangeRimePicker" {...config}>
<RangePicker showTime format="YYYY-MM-DD" />
</FormBinder>
<FormError style={styles.formItemError} name="rangeRimePicker" />
</div>
<div style={styles.formItem}>
<span style={styles.formItemLabel}>时间选择:</span>
<FormBinder name="timePicker" {...config}>
<TimePicker format="HH:mm:ss" style={{...style}} />
</FormBinder>
<FormError style={styles.formItemError} name="timePicker" />
</div>
<Button type="primary" style={{marginLeft: '80px'}} onClick={this.validateFields}>
确 认
</Button>
</div>
</FormBinderWrapper>
<div style={styles.preview}>
<strong>当前表单数据</strong>
<pre>{JSON.stringify(this.state.value, null, 2)}</pre>
</div>
</div>
);
}
}
const styles = {
formItem: {
marginBottom: '20px'
},
formItemLabel: {
marginRight: '10px',
},
formItemError: {
marginLeft: '10px',
},
preview: {
border: '1px solid #eee',
marginTop: 20,
padding: 10
}
}
ReactDOM.render(<Time />, mountNode);
经过 FormBinder 包裹的 Select 组件,重置值为未选择状态。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Select, Button, Grid } from '@alifd/next';
const { Row, Col } = Grid;
const dataSource = [];
class Reset extends Component {
constructor(props) {
super(props);
this.state = {
value: {
bu: 'taobao',
},
};
}
formChange = value => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value });
};
handleReset = () => {
this.setState({
value: {
bu: 'taobao',
},
});
};
render() {
return (
<div>
<FormBinderWrapper value={this.state.value} onChange={this.formChange} ref="form">
<div style={styles.content}>
<div style={styles.formItem}>
<span>请选择:</span>
<FormBinder name="bu" required message="请选择">
<Select
dataSource={[
{
value: 'taobao',
label: '淘宝',
},
{
value: 'tmall',
label: '天猫',
},
{
value: 'aliyun',
label: '阿里云',
},
{
value: 'alitrip',
label: '飞猪',
},
]}
placeholder="请选择"
autoWidth={false}
/>
</FormBinder>
<FormError name="bu" />
</div>
<Button type="primary" onClick={this.handleReset} style={styles.resetButton}>重 置</Button>
</div>
</FormBinderWrapper>
<div style={styles.preview}>
<strong>当前表单数据:</strong>
<pre>
{JSON.stringify(this.state.value, null, 2)}
</pre>
</div>
</div>
);
}
}
const styles = {
formItem: {
display: 'flex',
alignItems: 'center',
marginBottom: '20px',
},
resetButton: {
marginLeft: '56px',
},
preview: {
border: '1px solid #eee',
margin: '20px 0',
padding: '10px'
}
}
ReactDOM.render(<Reset />, mountNode);
在表单检验中,可以分步骤对表单进行校验,先校验一部分表单域通过后在检验另外一部分表单
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Input, Button } from '@alifd/next';
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: {
email: '',
name: '',
password: '',
}
};
}
formChange = value => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value });
};
validateFields = (fieldnames = []) => {
const cb = (errors, values) => {
console.log('validateFields:', errors, values)
}
const { validateFields } = this.refs.form;
if (fieldnames.length) {
validateFields(fieldnames, cb);
} else {
validateFields(cb);
}
}
render() {
return (
<div>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
ref="form"
>
<div>
<div style={styles.formItem}>
<span style={styles.formLabel}>名称:</span>
<FormBinder name="name" required message="请输入正确的名称" >
<Input placeholder="淘小宝" />
</FormBinder>
<FormError style={styles.formError} name="name" />
</div>
<div style={styles.formItem}>
<span style={styles.formLabel}>邮箱:</span>
<FormBinder name="email" type="email" required message="请输入正确的邮箱">
<Input placeholder="ice-admin@alibaba-inc.com" />
</FormBinder>
<FormError style={styles.formError} name="email" />
</div>
<div style={styles.formItem}>
<span style={styles.formLabel}>设置密码:</span>
<FormBinder name="password" required message="请输入新密码" >
<Input htmlType="password" placeholder="设置新密码" />
</FormBinder>
<FormError style={styles.formError} name="password" />
</div>
</div>
</FormBinderWrapper>
<div style={{marginTop: 20}}>
<Button type="primary" style={{marginRight: 10}} onClick={() => this.validateFields(['name', 'email'])}>
先校验名称和邮箱
</Button>
<Button type="secondary" onClick={this.validateFields}>
校验整个表单
</Button>
</div>
<div style={styles.preview}>
<strong>当前表单数据</strong>
<pre>{JSON.stringify(this.state.value, null, 2)}</pre>
</div>
</div>
);
}
}
const styles = {
formItem: {
marginBottom: '20px'
},
formLabel: {
width: '70px',
marginRight: '10px',
textAlign: 'right',
display: 'inline-block'
},
formError: {
marginLeft: '10px',
},
preview: {
border: '1px solid #eee',
marginTop: 20,
padding: 10
}
}
ReactDOM.render(<App />, mountNode);
可以使用 validator 自定义校验,根据不同情况执行不同的校验规则
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Select, Button, Grid, Input } from '@alifd/next';
const { Row, Col } = Grid;
class CustomValidator extends Component {
constructor(props) {
super(props);
this.state = {
value: { input: '' },
};
}
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
formChange = (value) => {
this.setState({ value })
}
// 通过 validator 自定义校验规则,更多用法参考 https://github.com/yiminghe/async-validator#usage
inputValidator = (rule, value, callback) => {
const errors = [];
console.log(value)
if (!value) {
callback('输入不能为空');
} else if (value.length < 8) {
callback('输入长度必须大于 8 位');
} else if (value.length > 16) {
callback('输入长度必须小于 16 位');
} else {
callback();
}
};
render() {
return (
<div>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
ref="form"
>
<div style={styles.formItem}>
<span style={styles.formLabel}>自定义校验:</span>
<FormBinder name="input" required validator={this.inputValidator} >
<Input placeholder="请输入"/>
</FormBinder>
<FormError name="input" style={styles.formError} />
</div>
</FormBinderWrapper>
<p style={styles.desc}>输入不能为空,且长度必须大于8位小于16位</p>
<div style={styles.preview}>
<strong>当前表单数据:</strong>
<pre>
{JSON.stringify(this.state.value, null, 2)}
</pre>
</div>
</div>
);
}
}
const styles = {
formItem: {
dispaly: 'flex',
alignItems: 'center',
},
formLabel: {
marginRight: '10px'
},
formError: {
marginLeft: '10px',
},
desc: {
margin: '5px 0 20px 94px',
color: '#999',
fontSize: '12px'
},
preview: {
border: '1px solid #eee',
margin: '20px 0',
padding: '10px'
}
}
ReactDOM.render(<CustomValidator />, mountNode);
有时候自定义或第三方的表单组件的取值属性不是 value,可以通过 valuePropName 来进行修改,也可以通过 setFieldValue 和 getFieldValue 进行转换
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Checkbox, Switch } from '@alifd/next';
class App extends Component {
state = {
value: {
checkbox: true,
switch: 1
}
};
formChange = (value) => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value })
}
render() {
return (
<div>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
>
<div style={styles.content}>
<div style={styles.formItem}>
<span>复选框:</span>
<FormBinder name="checkbox" valuePropName="checked">
<Checkbox />
</FormBinder>
</div>
<div style={styles.formItem}>
<span>开关:</span>
<FormBinder
name="switch"
valuePropName="checked" // Switch 接收的属性是 `checked`
setFieldValue={(selected) => { return selected === 1 }} // 转换为 boolean 传给 switch
getFieldValue={(checked) => { return checked ? 1 : 0 }} // 返回值转换为 number 给表单值
>
<Switch size="small" />
</FormBinder>
</div>
</div>
</FormBinderWrapper>
<div style={styles.preview}>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
</div>
)
}
}
const styles = {
formItem: {
display: 'flex',
alignItems: 'center',
marginBottom: '20px'
},
preview: {
border: '1px solid #eee',
marginTop: 20,
padding: 10
}
}
ReactDOM.render(<App />, mountNode);
演示 Input 组件数据交互和循环数组数据交互,数组表单用法,动态增加、减少表单项。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
import { Input, Button, Grid, DatePicker } from '@alifd/next';
const { Row, Col } = Grid;
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: {
items: [{}]
}
};
}
addItem = () => {
this.state.value.items.push({});
this.setState({ value: this.state.value });
};
formChange = value => {
// 说明:
// 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
// 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
this.setState({ value });
};
changeItem = () => {
let items = this.state.value.items;
items[0].aaa = '有趣';
this.setState({
value: {
...this.state.value,
items: items
}
});
};
removeItem = (index) => {
this.state.value.items.splice(index, 1);
this.setState({
value: this.state.value
});
}
validateAllFormField = () => {
this.refs.form.validateFields((errors, values) => {
console.log('errors', errors, 'values', values);
});
};
render() {
return (
<div>
<FormBinderWrapper
value={this.state.value}
onChange={this.formChange}
ref="form"
>
<ArticleList
items={this.state.value.items}
addItem={this.addItem}
removeItem={this.removeItem}
validateAllFormField={this.validateAllFormField}
/>
</FormBinderWrapper>
<div style={styles.preview}>
<strong>当前表单数据:</strong>
<pre>{JSON.stringify(this.state.value, null, 2)}</pre>
</div>
</div>
);
}
}
class ArticleList extends Component {
render() {
return (
<div>
{this.props.items.map((item, index) => {
return (
<Row key={index} style={styles.row}>
<Col>
<span>文章名称:</span>
<FormBinder required message="文章名称必填" name={`items[${index}].name`} >
<Input />
</FormBinder>
<FormError name={`items[${index}].name`} style={styles.formError} />
</Col>
<Col>
<span>文章地址:</span>
<FormBinder name={`items[${index}].url`} type="url" required message="请输入正确的 URL 地址" >
<Input />
</FormBinder>
<FormError name={`items[${index}].url`} style={styles.formError} />
</Col>
<Col>
<Button type="secondary" onClick={this.props.removeItem.bind(this, index)}>删除</Button>
</Col>
</Row>
);
})}
<div style={styles.buttons}>
<Button type="secondary" onClick={this.props.addItem}>新 增</Button>
<Button type="primary" style={{marginLeft: 10}} onClick={this.props.validateAllFormField}>
校 验
</Button>
</div>
</div>
);
}
}
const styles = {
row: {
marginBottom: '20px',
},
formError: {
display: 'block',
marginTop: '10px',
marginLeft: '70px',
},
buttons: {
margin: '20px 0 0 86px',
},
preview: {
border: '1px solid #eee',
marginTop: 20,
padding: 10
}
}
ReactDOM.render(<App />, mountNode);