Form 表单
如果项目中使用的是 1.x 版本的基础组件(@alifd/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @icedesign/base@latest -S
开发指南
何时使用
表单校验、数据提交操作是用到。 Form表单包含了布局、数据获取、校验功能等功能, 其中 数据获取
、校验功能
需要配合 Field
使用才能发挥。
注意事项
组件不要使用关键字
nodeName
作为 name、id使用了 Field
init
过的组件,请勿在组件上面直接定义ref
value
onChange
事件。Form 默认使用
size=medium
, 并且会控制FormItem内所有组件的size。 如果想修改组件的size<FormItem size="small" >
在垂直表单中如果文字(一般
<p>
标签)或者组件向上偏离,可以通过className="next-form-text-align"
辅助调整
API
表单
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
prefix | 样式前缀 | String | 'next-' |
direction | 表单展示方向可选值:'hoz'(水平)'ver'(垂直) | Enum | 'ver' |
size | 单个FormItem的size自定义,优先级高于Form的size, 并且当组件与 FormItem 一起使用时,组件自身设置 size 属性无效。可选值:'large'(大)'medium'(中)'small'(小) | Enum | 'medium' |
labelAlign | 标签的位置可选值:'top'(上)'left'(左)'inset'(内) | Enum | 'left' |
labelTextAlign | 标签的左右对齐方式可选值:'''left'(左)'right'(右) | Enum | - |
field | 经 new Field(this) 初始化后,直接传给 Form 即可 用到表单校验则不可忽略此项 | any | - |
onSubmit | form内有 htmlType="submit" 的元素的时候会触发签名:Function() => void | Function | function(e) { e.preventDefault(); } |
children | 子元素 | any | - |
className | 扩展class | String | - |
style | 自定义内联样式 | Object | - |
Form.Item
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
prefix | 样式前缀 | String | 'next-' |
label | label 标签的文本 | ReactNode | - |
labelCol | label 标签布局,通 <Col> 组件,设置 span offset 值,如 {span: 8, offset: 16},该项仅在垂直表单有效 | Object | - |
help | 提示信息,如不设置,则会根据校验规则自动生成. 如果设置会受控(ps: 可以利用这点自定义错误位置,详细看demo自定义错误) | ReactNode | - |
validateStatus | 校验状态,如不设置,则会根据校验规则自动生成可选值:'''success'(成功)'error'(失败)'loading'(校验中) | Enum | - |
hasFeedback | 配合 validateStatus 属性使用,是否展示校验状态图标, 目前只有Input支持 | Boolean | false |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | Object | - |
style | 自定义内联样式 | Object | - |
extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 位于错误信息后面 | ReactNode | - |
size | 单个FormItem的size自定义,优先级高于Form的size, 并且当组件与 FormItem 一起使用时,组件自身设置 size 属性无效。可选值:'', 'large', 'small', 'medium' | Enum | - |
className | 扩展class | String | - |
代码示例
配合 Row
Col
控制表单内元素布局标签位置:上、左
查看源码在线预览
import {
Form,
Input,
Switch,
Grid,
Button,
Icon,
Balloon,
Field
} from "@icedesign/base";
const { Row, Col } = Grid;
const FormItem = Form.Item;
const style = {
padding: "20px",
background: "#F7F8FA",
margin: "20px"
};
const formItemLayout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 }
};
const label = (
<span>
名称:<Balloon
type="primary"
trigger={<Icon type="prompt" size="small" />}
closable={false}
>
blablablablablablablabla
</Balloon>
</span>
);
class Demo extends React.Component {
constructor(props, context) {
super(props, context);
this.field = new Field(this);
}
render() {
const { init, getValue } = this.field;
return (
<div>
<h3>标签位置</h3>
<Switch
checkedChildren="左"
unCheckedChildren="上"
{...init("labelAlign", { initValue: false })}
/>
<Form
labelAlign={!getValue("labelAlign") ? "top" : "left"}
style={style}
>
<Row>
<Col>
<FormItem {...formItemLayout} label={label} required>
<Input placeholder="请输入搜索名称" />
</FormItem>
<FormItem {...formItemLayout} label="较长搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
<FormItem {...formItemLayout} label="搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} label="搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
<FormItem {...formItemLayout} label="较长搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
<FormItem {...formItemLayout} label="搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} label="搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
<FormItem {...formItemLayout} label="较长搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
<FormItem {...formItemLayout} label="搜索名称:">
<Input placeholder="请输入搜索名称" />
</FormItem>
</Col>
</Row>
<Row>
<Col style={{ textAlign: "right" }}>
<Button type="primary" style={{ marginRight: "5px" }}>
搜索
</Button>
<Button>清除条件</Button>
</Col>
</Row>
</Form>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
如果组件比较靠上,可以用 class=next-form-text-align
做调整拉伸浏览器的时候label宽度不变。
查看源码在线预览
import { Form, Input, Button, Checkbox, Field } from "@icedesign/base";
const FormItem = Form.Item;
class Demo extends React.Component {
constructor(props) {
super(props);
this.field = new Field(this);
}
handleSubmit() {
console.log("收到表单值:", this.field.getValues());
}
render() {
const init = this.field.init;
const formItemLayout = {
labelCol: {
fixedSpan: 10
},
wrapperCol: {
span: 14
}
};
return (
<Form direction="ver" field={this.field}>
<FormItem label="用户名:" {...formItemLayout}>
<p className="next-form-text-align">固定名称</p>
</FormItem>
<FormItem label="密码:" required {...formItemLayout}>
<Input
htmlType="password"
{...init("pass")}
placeholder="请输入密码"
/>
</FormItem>
<FormItem label="备注:" {...formItemLayout} help="随便写点什么">
<Input multiple placeholder="随便写" {...init("remark")} />
</FormItem>
<FormItem label=" " {...formItemLayout}>
<Checkbox {...init("agreement")}>同意</Checkbox>
</FormItem>
<FormItem label=" " {...formItemLayout}>
<Button type="primary" onClick={this.handleSubmit.bind(this)}>
确定
</Button>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
通过设置labelAlign="inset"
(只适用于Input、Select组件,其他组件不适用)
查看源码在线预览
import { Form, Input, Select } from "@icedesign/base";
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 }
};
ReactDOM.render(
<div>
<h3>垂直</h3>
<Form labelAlign="inset" style={{ maxWidth: "500px" }}>
<FormItem {...formItemLayout} label="账户:">
<Input placeholder="请输入账户名" id="userName" name="userName" />
</FormItem>
<FormItem {...formItemLayout} label="密码:">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem {...formItemLayout} label="密码:" validateStatus="error">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem {...formItemLayout} label="大小:">
<Select>
<div value="small">small</div>
<div value="medium">medium</div>
<div value="large">large</div>
</Select>
</FormItem>
</Form>
<h2>水平</h2>
<Form labelAlign="inset" direction="hoz">
<FormItem label="账户:">
<Input placeholder="请输入账户名" id="userName" name="userName" />
</FormItem>
<FormItem label="密码:">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem label="密码:" validateStatus="error" help="密码不能为空">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem label="大小:">
<Select style={{ width: 150 }}>
<div value="small">small</div>
<div value="medium">medium</div>
<div value="large">large</div>
</Select>
</FormItem>
</Form>
</div>,
mountNode
);
展示和表单相关的其他组件。
查看源码在线预览
import {
Form,
Input,
Button,
Checkbox,
Select,
Range,
Balloon,
DatePicker,
TimePicker,
NumberPicker,
Field,
Switch,
Upload,
Grid
} from "@icedesign/base";
const { RangePicker } = DatePicker;
const { Row, Col } = Grid;
const FormItem = Form.Item;
class Demo extends React.Component {
field = new Field(this);
handleSubmit(e) {
e.preventDefault();
console.log("收到表单值:", this.field.getValues());
this.field.validate();
}
getValueFromFile(e) {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}
render() {
const init = this.field.init;
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 }
};
return (
<Form field={this.field}>
<FormItem label="密码:" {...formItemLayout}>
<Balloon
trigger={<Input htmlType="password" />}
align="r"
closable={false}
triggerType="hover"
>
input password
</Balloon>
</FormItem>
<FormItem label="我是标题:" {...formItemLayout}>
<p className="next-form-text-align">唧唧复唧唧木兰当户织呀</p>
<p className="next-form-text-align">
<a href="#">链接文字</a>
</p>
</FormItem>
<FormItem label="NumberPicker 数字输入框:" {...formItemLayout}>
<NumberPicker
min={1}
max={10}
{...init("numberPicker", { initValue: 3 })}
/>
<span> 台机器</span>
</FormItem>
<FormItem label="Switch 开关:" {...formItemLayout} required>
<Switch
{...init("switch", { valueName: "checked", initValue: true })}
/>
</FormItem>
<FormItem label="Range 滑动输入条:" {...formItemLayout} required>
<Range
defaultValue={30}
scales={[0, 100]}
style={{ marginTop: "10px" }}
marks={[0, 100]}
{...init("range")}
/>
</FormItem>
<FormItem label="Select 选择器:" {...formItemLayout} required>
<Select style={{ width: 200 }} {...init("select")}>
<Option value="jack">jack</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>
disabled
</Option>
<Option value="hugohua">hugohua</Option>
</Select>
</FormItem>
<FormItem
label="DatePicker 日期选择框:"
labelCol={{ span: 6 }}
required
>
<Row>
<FormItem style={{ marginRight: 10 }}>
<DatePicker {...init("startDate")} />
</FormItem>
<FormItem>
<DatePicker {...init("endDate")} />
</FormItem>
</Row>
</FormItem>
<FormItem
label="RangePicker 范围选择框:"
labelCol={{ span: 6 }}
required
>
<RangePicker {...init("rangeDate")} />
</FormItem>
<FormItem label="TimePicker 时间选择器:" {...formItemLayout} required>
<TimePicker
{...init("time", {
getValueFromEvent: time => {
time =
time &&
time.toLocaleTimeString("zh-CN", {
hour12: false
});
return time;
}
})}
/>
</FormItem>
<FormItem
className="next-form-text-align"
label="Checkbox 多选框:"
{...formItemLayout}
>
<Checkbox {...init("checkbox1")}>选项一 </Checkbox>
<Checkbox {...init("checkbox2")}>选项二 </Checkbox>
<Checkbox disabled {...init("checkbox3")}>
选项三(不可选)
</Checkbox>
</FormItem>
<FormItem label="logo图:" {...formItemLayout}>
<Upload
action="/upload.do"
listType="text"
onChange={this.handleUpload}
{...init("upload", {
valueName: "fileList",
getValueFromEvent: this.getValueFromFile
})}
>
<Button type="primary" style={{ margin: "0 0 10px" }}>
上传文件
</Button>
</Upload>
</FormItem>
<Row style={{ marginTop: 24 }}>
<Col offset="6">
<Button type="primary" onClick={this.handleSubmit.bind(this)}>
确定
</Button>
</Col>
</Row>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
FormItem嵌套
查看源码在线预览
import { Form, Input, Button, Field, Grid } from "@icedesign/base";
const { Row, Col } = Grid;
const FormItem = Form.Item;
class Demo extends React.Component {
field = new Field(this);
handleSubmit(e) {
e.preventDefault();
console.log("收到表单值:", this.field.getValues());
}
normFile(e) {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}
render() {
const init = this.field.init;
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 }
};
const insetLayout = {
labelCol: { fixedSpan: 4 }
};
return (
<Form field={this.field}>
<FormItem id="control-input" label="输入框:" {...formItemLayout}>
<Row>
<Col>
<FormItem
label="内嵌模式"
required={false}
labelAlign="inset"
{...insetLayout}
>
<Input
placeholder="Please enter..."
style={{ width: "100%" }}
{...init("firstname", {
rules: [{ required: true, trigger: "onBlur" }]
})}
/>
</FormItem>
</Col>
<Col>
<FormItem
label="内嵌模式"
required={false}
labelAlign="inset"
{...insetLayout}
>
<Input
placeholder="need onChange"
style={{ width: "100%" }}
{...init("secondname", {
rules: [{ required: true }]
})}
/>
</FormItem>
</Col>
</Row>
</FormItem>
<FormItem label="银行账户:" {...formItemLayout}>
<Row>
<Col>
<FormItem>
<Input
{...init("A", {
rules: [{ required: true, trigger: "onBlur" }]
})}
/>
</FormItem>
</Col>
<Col>
<FormItem>
<Input
{...init("B", {
rules: [{ required: true, trigger: "onBlur" }]
})}
/>
</FormItem>
</Col>
<Col>
<FormItem>
<Input
{...init("C", {
rules: [{ required: true, trigger: "onBlur" }]
})}
/>
</FormItem>
</Col>
<Col>
<FormItem>
<Input
{...init("D", {
rules: [{ required: true, trigger: "onBlur" }]
})}
/>
</FormItem>
</Col>
</Row>
</FormItem>
<Row style={{ marginTop: 24 }}>
<Col offset="6">
<Button type="primary" onClick={this.handleSubmit.bind(this)}>
确定
</Button>
</Col>
</Row>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
需要Form里面有 htmlType="submit" 的元素
查看源码在线预览
import { Form, Input, Button } from "@icedesign/base";
const FormItem = Form.Item;
class Demo extends React.Component {
onSubmit(e) {
e.preventDefault();
console.log("onsubmit");
}
render() {
return (
<Form onSubmit={this.onSubmit.bind(this)}>
<FormItem>
<Input placeholder="回车也能触发onSubmit" />
</FormItem>
<Button htmlType="submit">submit</Button>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
如果需要自己控制错误位置,可以让help=""
然后自己放置展示错误的地方在redux
中结合 componentWillReceiveProps
setErrors
使用
查看源码在线预览
import { Form, Input, Button, Field } from "@icedesign/base";
import { combineReducers, createStore } from "redux";
import { Provider, connect } from "react-redux";
const initState = {
values: { email: "", username: "xiachi" },
errors: {}
};
function formReducer(state = initState, action) {
switch (action.type) {
case "save_fields":
return {
...state,
values: {
...state.values,
...action.values
}
};
case "set_errors":
return {
...state,
errors: {
...state.errors,
...action.errors
}
};
default:
return state;
}
}
class FormDemo extends React.Component {
static propTypes = {
formData: React.PropTypes.object,
email: React.PropTypes.object,
dispatch: React.PropTypes.func
};
constructor(props) {
super(props);
this.field = new Field(this, {
onChange: (name, value) => {
console.log("onChange", name, value, this.field.getError(name));
this.props.dispatch({
type: "save_fields",
values: {
[name]: value
}
});
this.props.dispatch({
type: "set_errors",
errors: {
[name]: this.field.getError(name)
}
});
}
});
}
componentWillReceiveProps(nextProps) {
this.field.setValues(nextProps.formData.values);
this.field.setErrors(nextProps.formData.errors);
}
setEmail() {
this.props.dispatch({
type: "save_fields",
values: {
email: "qq@gmail.com"
}
});
}
setName() {
this.props.dispatch({
type: "save_fields",
values: {
username: "frank"
}
});
}
setError() {
this.props.dispatch({
type: "set_errors",
errors: {
email: "来自远程的错误消息"
}
});
}
setErrors() {
this.props.dispatch({
type: "set_errors",
errors: {
email: "来自远程的错误消息A",
username: "来自远程的错误消息B"
}
});
}
render() {
const init = this.field.init;
return (
<Form field={this.field}>
<Form.Item>
<Input
{...init("email", {
initValue: this.props.formData.values.email,
rules: [{ required: true, type: "email" }]
})}
/>
</Form.Item>
<Form.Item help="">
<Input
{...init("username", {
initValue: this.props.formData.values.username,
rules: [{ required: true, message: "不能为空" }]
})}
/>
<p style={{ color: "blue" }}>{this.field.getError("username")}</p>
</Form.Item>
<p>email: {this.props.email && this.props.email.value}</p>
<Button onClick={this.setEmail.bind(this)}>setEmail</Button>
<Button onClick={this.setName.bind(this)}>setName</Button>
<Button onClick={this.setError.bind(this)}>setError</Button>
<Button onClick={this.setErrors.bind(this)}>setErrors</Button>
<Button onClick={() => this.field.reset()}>reset</Button>
</Form>
);
}
}
const ReduxFormDemo = connect(state => {
return {
formData: state.formReducer
};
})(FormDemo);
const store = createStore(
combineReducers({
formReducer
})
);
ReactDOM.render(
<Provider store={store}>
<div>
<ReduxFormDemo />
</div>
</Provider>,
mountNode
);
在redux
中结合 componentWillReceiveProps
setValues
使用
查看源码在线预览
import { Form, Input, Button, Checkbox, Field } from "@icedesign/base";
import { combineReducers, createStore } from "redux";
import { Provider, connect } from "react-redux";
const CheckboxGroup = Checkbox.Group;
function formReducer(
state = { email: "", username: "xiachi", fruit: ["apple"] },
action
) {
switch (action.type) {
case "save_fields":
return {
...state,
...action.payload
};
default:
return state;
}
}
class FormDemo extends React.Component {
static propTypes = {
formData: React.PropTypes.object,
email: React.PropTypes.object,
dispatch: React.PropTypes.func
};
constructor(props) {
super(props);
this.field = new Field(this, {
onChange: (name, value) => {
console.log("onChange", name, value, this.field.getValues());
this.props.dispatch({
type: "save_fields",
payload: {
[name]: value
}
});
/* 方法2, 所有值全部更新
this.props.dispatch({
type: 'save_fields',
payload: this.field.getValues()
});
*/
}
});
}
componentWillReceiveProps(nextProps) {
this.field.setValues(nextProps.formData);
}
setEmail() {
this.props.dispatch({
type: "save_fields",
payload: {
email: "qq@gmail.com"
}
});
}
setName() {
this.props.dispatch({
type: "save_fields",
payload: {
username: "frank"
}
});
}
setGroup() {
this.props.dispatch({
type: "save_fields",
payload: {
fruit: ["pear"]
}
});
}
render() {
const init = this.field.init;
return (
<Form field={this.field}>
<Form.Item>
<Input
{...init("email", {
rules: [{ required: true, message: "不能为空!" }]
})}
/>
</Form.Item>
<Form.Item>
<Input
defaultValue={this.props.formData.username}
{...init("username", {
rules: [{ required: true, message: "不能为空" }]
})}
/>
</Form.Item>
<Form.Item>
<CheckboxGroup
dataSource={[
{ label: "苹果", value: "apple" },
{ label: "梨", value: "pear" }
]}
defaultValue={this.props.formData.fruit}
{...init("fruit", {
rules: [{ required: true, type: "array", message: "不能为空" }]
})}
/>
</Form.Item>
<p>email: {this.props.email && this.props.email.value}</p>
<Button onClick={this.setEmail.bind(this)}>setEmail</Button>
<Button onClick={this.setName.bind(this)}>setName</Button>
<Button onClick={this.setGroup.bind(this)}>setGroup</Button>
</Form>
);
}
}
const ReduxFormDemo = connect(state => {
return {
formData: state.formReducer
};
})(FormDemo);
const store = createStore(
combineReducers({
formReducer
})
);
ReactDOM.render(
<Provider store={store}>
<div>
<ReduxFormDemo />
</div>
</Provider>,
mountNode
);
labelTextAlign
文字左右对齐方式labelAlign
label方位size
会强制设置 FormItem
下的所有组件的size
查看源码在线预览
import { Form, Input, Select, Field } from "@icedesign/base";
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 }
};
class Demo extends React.Component {
constructor(props, context) {
super(props, context);
this.field = new Field(this);
}
render() {
const { init, getValue } = this.field;
return (
<div>
<Form direction="hoz" className="demo-ctl" field={this.field}>
<FormItem label="大小:">
<Select {...init("size", { initValue: "medium" })}>
<div value="small">small</div>
<div value="medium">medium</div>
<div value="large">large</div>
</Select>
</FormItem>
<FormItem label="标签对齐方式:" help="inset只能用于 Input、Select">
<Select {...init("labelAlign", { initValue: "left" })}>
<div value="top">标签在上 top</div>
<div value="left">标签在左 left</div>
<div value="inset">标签在内 inset</div>
</Select>
</FormItem>
<FormItem label="标签左右对齐方式:">
<Select {...init("labelTextAlign")}>
<div value="">系统自动</div>
<div value="left">left</div>
<div value="right">right</div>
</Select>
</FormItem>
</Form>
<h3>垂直</h3>
<Form
field={this.field}
size={getValue("size")}
labelAlign={getValue("labelAlign")}
labelTextAlign={getValue("labelTextAlign")}
style={{ maxWidth: "500px" }}
>
<FormItem {...formItemLayout} label="账户:">
<Input placeholder="请输入账户名" id="userName" name="userName" />
</FormItem>
<FormItem {...formItemLayout} required label="密码:">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem {...formItemLayout} label="密码:" validateStatus="error">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem {...formItemLayout} label="大小:">
<Select style={{ width: "100%" }} {...init("size")}>
<div value="small">small</div>
<div value="medium">medium</div>
<div value="large">large</div>
</Select>
</FormItem>
</Form>
<h3>水平</h3>
<Form
size={getValue("size")}
direction="hoz"
labelAlign={getValue("labelAlign")}
>
<FormItem label="账户:">
<Input placeholder="请输入账户名" id="userName" name="userName" />
</FormItem>
<FormItem label="密码:">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem label="密码:" validateStatus="error">
<Input
htmlType="password"
placeholder="请输入密码"
id="password"
name="password"
/>
</FormItem>
<FormItem label="大小:">
<Select style={{ width: "100%" }} {...init("size")}>
<div value="small">small</div>
<div value="medium">medium</div>
<div value="large">large</div>
</Select>
</FormItem>
</Form>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.demo-ctl {
background-color: #f1f1f1;
padding: 10.0px;
color: #0a7ac3;
border-left: 4.0px solid #0d599a;
}
基本的表单校验例子。
查看源码在线预览
import { Form, Input, Button, Radio, Field } from "@icedesign/base";
const { Item: FormItem } = Form;
const { Group: RadioGroup } = Radio;
class BasicDemo extends React.Component {
constructor(props) {
super(props);
this.field = new Field(this);
}
handleReset(e) {
e.preventDefault();
this.field.reset();
}
handleSubmit(e) {
e.preventDefault();
this.field.validate((errors, values) => {
if (errors) {
console.log("Errors in form!!!");
return;
}
console.log("Submit!!!");
console.log(values);
});
}
userExists(rule, value, callback) {
if (!value) {
callback();
} else {
setTimeout(() => {
if (value === "frank") {
callback([new Error("抱歉,该用户名已被占用。")]);
} else {
callback();
}
}, 1000);
}
}
checkPass(rule, value, callback) {
const { validate } = this.field;
if (value) {
validate(["rePasswd"]);
}
callback();
}
checkPass2(rule, value, callback) {
const { getValue } = this.field;
if (value && value !== getValue("passwd")) {
callback("两次输入密码不一致!");
} else {
callback();
}
}
render() {
const { init, getError, getState } = this.field;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
return (
<Form field={this.field}>
<FormItem
label="用户名:"
{...formItemLayout}
hasFeedback
help={
getState("name") === "validating"
? "校验中..."
: (getError("name") || []).join(", ")
}
>
<Input
maxLength={20}
hasLimitHint
placeholder="实时校验,输入 frank 看看"
{...init("name", {
rules: [
{ required: true, min: 5, message: "用户名至少为 5 个字符" },
{ validator: this.userExists }
]
})}
/>
</FormItem>
<FormItem label="邮箱:" {...formItemLayout} hasFeedback>
<Input
type="email"
placeholder="onBlur 与 onChange 相结合"
{...init("email", {
rules: [
{ required: true, trigger: "onBlur" },
{
type: "email",
message: <span>请输入正确的邮箱地址</span>,
trigger: ["onBlur", "onChange"]
}
]
})}
/>
</FormItem>
<FormItem label="密码:" {...formItemLayout} hasFeedback>
<Input
htmlType="password"
{...init("passwd", {
rules: [
{ required: true, whitespace: true, message: "请填写密码" },
{ validator: this.checkPass.bind(this) }
]
})}
/>
</FormItem>
<FormItem label="确认密码:" {...formItemLayout} hasFeedback>
<Input
htmlType="password"
placeholder="两次输入密码保持一致"
{...init("rePasswd", {
rules: [
{
required: true,
whitespace: true,
message: "请再次输入密码"
},
{
validator: this.checkPass2.bind(this)
}
]
})}
/>
</FormItem>
<FormItem label="性别:" hasFeedback {...formItemLayout}>
<RadioGroup
{...init("radio", {
rules: [{ required: true, message: "请选择您的性别" }]
})}
>
<Radio value="male">男</Radio>
<Radio value="female">女</Radio>
</RadioGroup>
</FormItem>
<FormItem label="备注:" {...formItemLayout}>
<Input
multiple
maxLength={20}
hasLimitHint
placeholder="随便写"
{...init("textarea", {
rules: [{ required: true, message: "真的不打算写点什么吗?" }]
})}
/>
</FormItem>
<FormItem wrapperCol={{ offset: 6 }}>
<Button type="primary" onClick={this.handleSubmit.bind(this)}>
确定
</Button>
<Button onClick={this.handleReset.bind(this)}>重置</Button>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<BasicDemo />, mountNode);
这里使用了 validation 的 validate(fields, callback)
方法,在对第一次输入的密码进行校验时会触发二次密码的校验。密码校验实例。
查看源码在线预览
import { Form, Input, Button, Dialog, Field, Grid } from "@icedesign/base";
import classNames from "classnames";
const { Row, Col } = Grid;
const FormItem = Form.Item;
function noop() {
return false;
}
class Demo extends React.Component {
constructor(props, context) {
super(props, context);
this.field = new Field(this);
this.state = {
passBarShow: false, // 是否显示密码强度提示条
rePassBarShow: false,
passStrength: "L", // 密码强度
rePassStrength: "L",
visible: false
};
}
handleSubmit() {
this.field.validate((errors, values) => {
if (errors) {
console.log("Errors in form!!!");
return;
}
console.log("Submit!!!");
console.log(values);
this.setState({ visible: false });
});
}
getPassStrenth(value, type) {
if (value) {
let strength;
// 密码强度的校验规则自定义,这里只是做个简单的示例
if (value.length < 6) {
strength = "L";
} else if (value.length <= 9) {
strength = "M";
} else {
strength = "H";
}
if (type === "pass") {
this.setState({ passBarShow: true, passStrength: strength });
} else {
this.setState({ rePassBarShow: true, rePassStrength: strength });
}
} else if (type === "pass") {
this.setState({ passBarShow: false });
} else {
this.setState({ rePassBarShow: false });
}
}
showDialog() {
this.setState({ visible: true });
}
hideDialog() {
this.setState({ visible: false });
}
checkPass(rule, value, callback) {
const field = this.field;
this.getPassStrenth(value, "pass");
if (field.getValue("pass")) {
field.validate(["rePass"], { force: true });
}
callback();
}
checkPass2(rule, value, callback) {
const field = this.field;
this.getPassStrenth(value, "rePass");
if (value && value !== field.getValue("pass")) {
callback("两次输入密码不一致!");
} else {
callback();
}
}
renderPassStrengthBar(type) {
const strength =
type === "pass" ? this.state.passStrength : this.state.rePassStrength;
const classSet = classNames({
"pwd-strength": true,
"pwd-strength-low": strength === "L",
"pwd-strength-medium": strength === "M",
"pwd-strength-high": strength === "H"
});
const level = {
L: "低",
M: "中",
H: "高"
};
return (
<div>
<ul className={classSet}>
<li className="pwd-strength-item pwd-strength-item-1" />
<li className="pwd-strength-item pwd-strength-item-2" />
<li className="pwd-strength-item pwd-strength-item-3" />
<span>{level[strength]}</span>
</ul>
</div>
);
}
render() {
const init = this.field.init;
return (
<div>
<Button type="primary" onClick={this.showDialog.bind(this)}>
修改密码
</Button>
<Dialog
title="修改密码"
visible={this.state.visible}
onOk={this.handleSubmit.bind(this)}
onCancel={this.hideDialog.bind(this)}
onClose={this.hideDialog.bind(this)}
>
<Form field={this.field} style={{ width: 500 }}>
<Row>
<Col span="16">
<FormItem
label="密码:"
labelCol={{ span: 10 }}
wrapperCol={{ span: 14 }}
>
<Input
htmlType="password"
{...init("pass", {
rules: [
{
required: true,
whitespace: true,
message: "请填写密码"
},
{ validator: this.checkPass.bind(this) }
]
})}
onContextMenu={noop}
onPaste={noop}
onCopy={noop}
onCut={noop}
autoComplete="off"
id="pass"
/>
</FormItem>
</Col>
<Col span="8">
{this.state.passBarShow
? this.renderPassStrengthBar("pass")
: null}
</Col>
</Row>
<Row>
<Col span="16">
<FormItem
label="确认密码:"
labelCol={{ span: 10 }}
wrapperCol={{ span: 14 }}
>
<Input
htmlType="password"
{...init("rePass", {
rules: [
{
required: true,
whitespace: true,
message: "请再次输入密码"
},
{
validator: this.checkPass2.bind(this)
}
]
})}
onContextMenu={noop}
onPaste={noop}
onCopy={noop}
onCut={noop}
autoComplete="off"
id="rePass"
/>
</FormItem>
</Col>
<Col span="8">
{this.state.rePassBarShow
? this.renderPassStrengthBar("rePass")
: null}
</Col>
</Row>
</Form>
</Dialog>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.pwd-strength {
display: inline-block;
margin-left: 8px;
margin-top: 0;
line-height: 32px;
height: 32px;
vertical-align: middle;
}
.pwd-strength-item {
float: left;
margin-right: 1px;
margin-top: 12px;
width: 19px;
height: 8px;
line-height: 8px;
list-style: none;
background-color: #f3f3f3;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.pwd-strength-item-1 {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
.pwd-strength-item-2 {
width: 20px;
}
.pwd-strength-item-3 {
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
margin-right: 8px;
}
.pwd-strength-low .pwd-strength-item-1, .pwd-strength-medium .pwd-strength-item-1, .pwd-strength-high .pwd-strength-item-1 {
background-color: #FAC450;
}
.pwd-strength-medium .pwd-strength-item-2, .pwd-strength-high .pwd-strength-item-2 {
background-color: rgba(135, 208, 104, .6);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#9987D068,endColorstr=#9987D068);
}
.pwd-strength-high .pwd-strength-item-3 {
background-color: #87D068;
}
Select
Radio
DatePicker
NumberPicker
。提供以下组件表单域的校验。
查看源码在线预览
import {
Form,
Button,
Radio,
Select,
DatePicker,
NumberPicker,
Field
} from "@icedesign/base";
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
class Demo extends React.Component {
constructor(props) {
super(props);
this.field = new Field(this);
}
handleReset(e) {
e.preventDefault();
this.field.reset();
}
handleSubmit(e) {
e.preventDefault();
this.field.validate((errors, values) => {
if (errors) {
console.log("Errors in form!!!");
return;
}
console.log("Submit!!!");
console.log(values);
});
}
checkBirthday(rule, value, callback) {
console.log(value);
if (value && value.getTime() >= Date.now()) {
callback(new Error("你不可能在未来出生吧!"));
} else {
callback();
}
}
checkPrime(rule, value, callback) {
if (value !== 11) {
callback(new Error("8~12之间的质数明明是11啊!"));
} else {
callback();
}
}
render() {
const init = this.field.init;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
return (
<Form field={this.field}>
<FormItem label="国籍:" {...formItemLayout}>
<Select
placeholder="请选择国家"
style={{ width: 200 }}
{...init("select", {
rules: [{ required: true, message: "请选择您的国籍" }]
})}
>
<li value="china">中国</li>
<li value="use">美国</li>
<li value="japan">日本</li>
<li value="korean">韩国</li>
<li value="Thailand">泰国</li>
</Select>
</FormItem>
<FormItem label="喜欢的颜色:" {...formItemLayout}>
<Select
multiple
placeholder="请选择颜色"
style={{ width: 200 }}
{...init("multiSelect", {
rules: [{ required: true, message: "请选择您喜欢的颜色" }]
})}
>
<li value="red">红色</li>
<li value="orange">橙色</li>
<li value="yellow">黄色</li>
<li value="green">绿色</li>
<li value="blue">蓝色</li>
</Select>
</FormItem>
<FormItem label="性别:" hasFeedback {...formItemLayout}>
<RadioGroup
{...init("radio", {
rules: [{ required: true, message: "请选择您的性别" }]
})}
>
<Radio value="male">男</Radio>
<Radio value="female">女</Radio>
</RadioGroup>
</FormItem>
<FormItem label="生日:" {...formItemLayout}>
<DatePicker
{...init("birthday", {
rules: [
{
required: true,
type: "date",
message: "你的生日是什么呢?"
},
{
validator: this.checkBirthday
}
]
})}
/>
</FormItem>
<FormItem label="8~12间的质数:" {...formItemLayout}>
<NumberPicker
min={8}
max={12}
{...init("primeNumber", {
rules: [{ validator: this.checkPrime }]
})}
/>
</FormItem>
<FormItem wrapperCol={{ span: 16, offset: 6 }}>
<Button type="primary" onClick={this.handleSubmit.bind(this)}>
确定
</Button>
<Button onClick={this.handleReset.bind(this)}>重置</Button>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
注意: 反馈图标只对 <Input />
有效。如果是 <Input>
组件, 可在<FormItem>
上面添加 hasFeedback
控制图标的展示为 <FormItem>
定义 validateStatus
属性控制三种校验状态。
查看源码在线预览
import {
Form,
Input,
DatePicker,
Radio,
NumberPicker,
Select
} from "@icedesign/base";
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
ReactDOM.render(
<Form>
<FormItem
label="失败校验:"
{...formItemLayout}
validateStatus="error"
help="请输入数字和字母组合"
>
<Input defaultValue="无效选择" id="error" />
</FormItem>
<FormItem
label="校验中:"
{...formItemLayout}
hasFeedback
validateStatus="loading"
help="信息审核中..."
>
<Input defaultValue="我是被校验的内容" id="loading" />
</FormItem>
<FormItem
label="成功校验:"
{...formItemLayout}
hasFeedback
validateStatus="success"
>
<Input defaultValue="我是正文" id="success" />
</FormItem>
<FormItem
label="失败校验:"
{...formItemLayout}
hasFeedback
validateStatus="error"
help="请输入数字和字母组合"
>
<Input defaultValue="无效选择" id="error" />
</FormItem>
<FormItem
label="Datepicker:"
{...formItemLayout}
validateStatus="error"
help="请选择正确日期"
>
<DatePicker />
</FormItem>
<FormItem label="性别:" validateStatus="success" {...formItemLayout}>
<RadioGroup>
<Radio value="male">男</Radio>
<Radio value="female">女</Radio>
</RadioGroup>
</FormItem>
<FormItem
label="年龄:"
validateStatus="error"
{...formItemLayout}
help="请选择国家"
>
<Select placeholder="请选择国家">
<li value="china">中国</li>
<li value="use">美国</li>
<li value="japan">日本</li>
<li value="korean">韩国</li>
<li value="Thailand">泰国</li>
</Select>
</FormItem>
<FormItem label="国家:" validateStatus="error" {...formItemLayout}>
<NumberPicker />
</FormItem>
</Form>,
mountNode
);