表单组件。
表单布局、校验、数据提交操作时用到。 组件的设计思想可以看这篇文章 https://zhuanlan.zhihu.com/p/56280821
nodeName
作为 name、idsize=medium
, 并且会控制 FormItem 内所有组件的size。 如果想修改组件的size <FormItem size="small" >
<p>
标签)或者组件向上偏离,可以通过 className="next-form-text-align"
辅助调整<FormItem>
直接包裹的组件才能展示校验错误提示。如果界面不展示错误信息,请检查是否有多个层级。 比如 <FormItem><div><Input/></div></FormItem>
是无法展示校验信息的。<Form field={false}>
来关闭数据获取,变成一个纯布局组件Field
组件文档的 rules如果您的表单场景非常复杂,比如动态渲染,大量字段,复杂数据结构,复杂联动校验,可以考虑使用 formily,formily已经封装了所有fusion组件,保证您开箱即用
表单布局、编辑、提交、校验的基本使用
import { Form, Input, Checkbox } from '@alifd/next';
const FormItem = Form.Item;const formItemLayout = {labelCol: {fixedSpan: 8},wrapperCol: {span: 14}};class Demo extends React.Component {constructor(...args) {super(...args);this.handleSubmit = (values, errors) => {console.log("value & errors", values, errors);};}render() {return (<Form style={{ width: "60%" }} {...formItemLayout} colon><FormItemlabel="Username:"requiredrequiredMessage="Please input your username!"><Input name="baseUser" /></FormItem><FormItemlabel="Password:"requiredrequiredMessage="Please input your password!"><Input.Password name="basePass" placeholder="Please Enter Password" /></FormItem><FormItem label=" " colon={false}><Checkbox name="agreement" defaultChecked>Agree</Checkbox></FormItem><FormItem label=" " colon={false}><Form.Submittype="primary"validateonClick={this.handleSubmit}style={{ marginRight: 8 }}>Submit</Form.Submit><Form.Reset>Reset</Form.Reset></FormItem></Form>);}}ReactDOM.render(<Demo />, mountNode);
inline 布局只支持横排
import { Form, Field, Input, Radio, Switch } from '@alifd/next';
function handleSubmit(v) {console.log(v);}const formItemLayout = {labelCol: { span: 4 },wrapperCol: { span: 14 }};const App = () => {const field = Field.useField({autoUnmount: false,values: { inline: false, labelAlign: "left" }});const inline = field.getValue("inline");const labelAlign = inline ? "left" : field.getValue("labelAlign");const layout = inline ? {} : formItemLayout;return (<Form field={field} inline={inline} labelAlign={labelAlign} {...layout}><Form.Item label="Inline Layout"><Switch name="inline" /></Form.Item>{inline ? null : (<Form.Item label="Label align"><Radio.Group shape="button" name="labelAlign"><Radio value="left">left</Radio><Radio value="top">top</Radio><Radio value="inset">inset</Radio></Radio.Group></Form.Item>)}<Form.Item label="Username:"><Input name="inlineUser" placeholder="first" /></Form.Item><Form.Item label="Password:" hasFeedback={false}><Input.Passwordname="inlinePass"placeholder="Please enter your password!"/></Form.Item><Form.Item label=" "><Form.Submit onClick={handleSubmit}>Submit</Form.Submit></Form.Item></Form>);};ReactDOM.render(<App />, mountNode);
size
会强制设置 FormItem
下的所有组件的size
labelAlign
label方位(如果不设置 labelCol 和 wrapperCol 那么默认是标签在上)
labelTextAlign
文字左右对齐方式
import { Form, Input, Select, Radio, NumberPicker, DatePicker, Switch, Button } from '@alifd/next';
const FormItem = Form.Item;const Option = Select.Option;const formItemLayout = {labelCol: { span: 8 },wrapperCol: { span: 16 }};class Demo extends React.Component {constructor(...args) {super(...args);this.state = {size: "medium"};this.handleChange = v => {this.setState({size: v});};}render() {return (<div><Form{...formItemLayout}size={this.state.size}style={{ maxWidth: "500px" }}><FormItem label="Size:"><Radio.Groupshape="button"value={this.state.size}onChange={this.handleChange}><Radio value="small">small</Radio><Radio value="medium">medium</Radio><Radio value="large">large</Radio></Radio.Group></FormItem><FormItem label="Input:"><Input placeholder="Please enter your user name" id="userName" /></FormItem><FormItem label="Select:"><Select><Select.Option value="test">test</Select.Option></Select></FormItem><FormItem label="NumberPicker:"><NumberPicker /></FormItem><FormItem label="DatePicker:"><DatePicker /></FormItem><FormItem label="Switch:"><Switch /></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 } from '@alifd/next';
const FormItem = Form.Item;const formItemLayout = {labelCol: { fixedSpan: 3 },wrapperCol: { span: 20 }};class Demo extends React.Component {constructor(...args) {super(...args);this.state = {code: "",second: 60};this.handleSubmit = (values, errors) => {if (errors) {return;}console.log("Get form value:", values);};this.sendCode = (values, errors) => {if (errors) {return;}this.setState({code: Math.floor(Math.random() * (999999 - 99999 + 1) + 99999)});setInterval(() => {this.setState({second: --this.state.second});}, 1000);};}render() {const { code } = this.state;return (<Formstyle={{ width: 400 }}{...formItemLayout}labelTextAlign="left"size="large"labelAlign="inset"><FormItem label="name" required asterisk={false}><Input name="username" trim defaultValue="frank" /></FormItem><FormItem label="phone" format="tel" required asterisk={false}><Inputname="phone"triminnerAfter={<Form.Submittexttype="primary"disabled={!!code}validate={["phone"]}onClick={this.sendCode}style={{ marginRight: 10 }}>{code ? `retry after ${this.state.second}s` : "send code"}</Form.Submit>}/></FormItem>{this.state.code ? (<FormItem label="code" required asterisk={false}><Input name="code" trim defaultValue={this.state.code} /></FormItem>) : null}<FormItem label=" "><Form.Submitstyle={{ width: "100%" }}type="primary"validateonClick={this.handleSubmit}>Submit</Form.Submit></FormItem></Form>);}}ReactDOM.render(<Demo />, mountNode);
FormItem 嵌套
import { Form, Input, Grid } from '@alifd/next';
const FormItem = Form.Item;const { Row, Col } = Grid;const formItemLayout = {labelCol: { span: 4 },wrapperCol: { span: 14 }};const insetLayout = {labelCol: { fixedSpan: 3 }};ReactDOM.render(<Form {...formItemLayout}><FormItem id="control-input" label="Input Something:"><Row gutter="4"><Col><FormItemstyle={{ margin: 0 }}label="Nest"labelAlign="inset"{...insetLayout}requiredrequiredTrigger="onBlur"asterisk={false}><Input placeholder="Please enter..." name="firstname" /></FormItem></Col><Col><FormItemstyle={{ margin: 0 }}label="Nest"labelAlign="inset"{...insetLayout}requiredasterisk={false}><Input placeholder="need onChange" name="secondname" /></FormItem></Col></Row></FormItem><FormItem label="Bank Account:"><Row gutter="4"><Col><FormItem required requiredTrigger="onBlur"><Input name="A" /></FormItem></Col><Col><FormItem required requiredTrigger="onBlur"><Input name="B" /></FormItem></Col><Col><FormItem required requiredTrigger="onBlur"><Input name="C" /></FormItem></Col><Col><FormItem required requiredTrigger="onBlur"><Input name="D" /></FormItem></Col></Row></FormItem><FormItem label=" "><Form.Submit validate onClick={v => console.log(v)}>Submit</Form.Submit></FormItem></Form>,mountNode);
标签位置:上、左
配合 Row
Col
控制表单内元素布局 (注意:FormItem非Form直接子元素需要不能直接直接在Form上设置布局)
import { Form, Input, Switch, Grid, Button, Icon, Balloon } from '@alifd/next';
const FormItem = Form.Item;const { Row, Col } = Grid;const style = {padding: "20px",background: "#F7F8FA",margin: "20px"};const formItemLayout = {labelCol: { fixedSpan: 4 }};const label = (<span>name:<Balloontype="primary"trigger={<Icon type="prompt" size="small" />}closable={false}>blablablablablablablabla</Balloon></span>);class Demo extends React.Component {constructor(...args) {super(...args);this.state = {labelAlign: "top"};this.handleChange = v => {this.setState({labelAlign: v ? "left" : "top"});};}render() {const labelAlign = this.state.labelAlign;return (<div><Form inline><Form.Item label="label Position"><SwitchcheckedChildren="left"unCheckedChildren="top"checked={this.state.labelAlign === "left"}onChange={this.handleChange}/></Form.Item></Form><Form style={style}><Row gutter="4"><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label={label}required><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem></Col><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem></Col><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem></Col></Row><Row><Col style={{ textAlign: "right" }}><Button type="primary" style={{ marginRight: "5px" }}>Search</Button><Button>Clear All</Button></Col></Row></Form><Form style={style}><Row gutter="4"><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label={label}required><Input placeholder="Enter a search name:" /></FormItem></Col><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem></Col><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem></Col><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem></Col><Col><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem></Col></Row><Row><Col style={{ textAlign: "right" }}><Button type="primary" style={{ marginRight: "5px" }}>Search</Button><Button>Clear All</Button></Col></Row></Form></div>);}}ReactDOM.render(<Demo />, mountNode);
可通过设置 device
responsive
实现响应式, 1.19.0+ 添加,仅支持ie10+
import { Form, Input, Switch, Grid, Button, Icon, Balloon, ResponsiveGrid, ConfigProvider, Box } from '@alifd/next';
const FormItem = Form.Item;const { Row, Col } = Grid;const style = {padding: "20px",background: "#F7F8FA",margin: "20px"};const formItemLayout = {labelWidth: 100,colSpan: 4};const label = (<span>name:<Balloontype="primary"trigger={<Icon type="prompt" size="small" />}closable={false}>blablablablablablablabla</Balloon></span>);class Demo extends React.Component {constructor(...args) {super(...args);this.state = {labelAlign: "top",device: "desktop"};this.handleChange = v => {this.setState({labelAlign: v ? "left" : "top"});};this.btn = device => {this.setState({device});};}render() {const labelAlign = this.state.labelAlign;return (<ConfigProvider device={this.state.device}><div><h3>Label Position</h3><SwitchcheckedChildren="left"unCheckedChildren="top"checked={this.state.labelAlign === "left"}onChange={this.handleChange}/><Button onClick={this.btn.bind(this, "desktop")}>desktop</Button><Button onClick={this.btn.bind(this, "tablet")}>tablet</Button><Button onClick={this.btn.bind(this, "phone")}>phone</Button><Form style={style} responsive><FormItem{...formItemLayout}labelAlign={labelAlign}label={label}required><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Long search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem{...formItemLayout}labelAlign={labelAlign}label="Search name:"><Input placeholder="Enter a search name:" /></FormItem><FormItem colSpan={12} style={{ textAlign: "right" }}><Button type="primary" style={{ marginRight: "5px" }}>Search</Button><Button>Clear All</Button></FormItem></Form></div></ConfigProvider>);}}ReactDOM.render(<Demo />, mountNode);
需要Form里面有 htmlType="submit" 的元素
import { Form, Input } from '@alifd/next';
const FormItem = Form.Item;class Demo extends React.Component {onSubmit(e) {e.preventDefault(); // form will auto submit if remove this lineconsole.log("onsubmit");}render() {return (<Form onSubmit={this.onSubmit.bind(this)}><FormItem><Input placeholder="Enter Key can also trigger ‘onSubmit’" /></FormItem><Form.Submit htmlType="submit">submit</Form.Submit></Form>);}}ReactDOM.render(<Demo />, mountNode);
可以通过配置 labelCol
wrapperCol
的 Grid.Col
响应式属性实现响应式
import { Form, Input, Select } from '@alifd/next';
const FormItem = Form.Item;const formItemLayout = {labelCol: { xxs: 4, l: 4 },wrapperCol: { xxs: 20, l: 16 }};ReactDOM.render(<Form {...formItemLayout}><FormItem label="userName:"><Input /></FormItem><FormItem label="password:"><InputhtmlType="password"name="resPass"placeholder="Please Enter Password"/></FormItem><FormItem label="Country:"><Select placeholder="Please select a country" style={{ width: "100%" }}><option value="china">China</option><option value="use">United States</option><option value="japan">Japan</option><option value="korean">South Korea</option><option value="Thailand">Thailand</option></Select></FormItem><FormItem label="Note:" help="something"><Input.TextArea placeholder="something" name="resReremark" /></FormItem><FormItem label=" "><Form.Submit>Submit</Form.Submit></FormItem></Form>,mountNode);
为 <FormItem>
定义 state
属性控制三种校验状态。
如果是 <Input>
组件, 可在<FormItem>
上面添加 hasFeedback
控制图标的展示
注意: 反馈图标只对 <Input />
有效。
import { Form, Input, DatePicker, TimePicker, NumberPicker, Select } from '@alifd/next';
const FormItem = Form.Item;const formItemLayout = {labelCol: {span: 6},wrapperCol: {span: 14}};ReactDOM.render(<Form {...formItemLayout}><FormItemlabel="Input Error:"validateState="error"help="Please enter a numeric and alphabetic string"><Input defaultValue="Invalid choice" /></FormItem><FormItemlabel="Loading:"hasFeedbackvalidateState="loading"help="Information Checking..."><Input defaultValue="Checking" /></FormItem><FormItem label="Success:" hasFeedback validateState="success"><Input defaultValue="Successful verification" /></FormItem><FormItemlabel="Datepicker:"validateState="error"help="Please select the correct date"><DatePicker /></FormItem><FormItemlabel="Timepicker:"validateState="error"help="Please select the correct time"><TimePicker /></FormItem><FormItemlabel="Select:"validateState="error"help="Please select a country"><Select placeholder="Please select a country"><option value="china">China</option><option value="use">United States</option><option value="japan">Japan</option><option value="korean">South Korea</option><option value="Thailand">Thailand</option></Select></FormItem><FormItem label="NumberPicker:" validateState="error"><NumberPicker defaultValue={0} /></FormItem></Form>,mountNode);
基本的表单校验例子。
import { Form, Input, Radio } from '@alifd/next';
const FormItem = Form.Item;const RadioGroup = Radio.Group;const formItemLayout = {labelCol: {span: 6},wrapperCol: {span: 14}};class BasicDemo extends React.Component {userExists(rule, value) {return new Promise((resolve, reject) => {if (!value) {resolve();} else {setTimeout(() => {if (value === "frank") {reject([new Error("Sorry, this username is already exist.")]);} else {resolve();}}, 500);}});}render() {return (<Form {...formItemLayout}><FormItemlabel="Account:"hasFeedbackvalidator={this.userExists.bind(this)}help=""><Input placeholder="Input frank" name="valUsername" /><Form.Error name="valUsername">{(errors, state) => {if (state === "loading") {return "loading...";} else {return errors;}}}</Form.Error></FormItem><FormItemlabel="Email:"hasFeedbackrequiredrequiredTrigger="onBlur"format="email"><Inputplaceholder="Both trigget onBlur and onChange"name="valEmail"/></FormItem><FormItemlabel="Password:"hasFeedbackrequiredrequiredMessage="Please enter password"><Input htmlType="password" name="valPasswd" /></FormItem><FormItemlabel="Gender:"hasFeedbackrequiredrequiredMessage="Please select your gender"><RadioGroup name="valSex"><Radio value="male">Male</Radio><Radio value="female">Female</Radio></RadioGroup></FormItem><FormItemlabel="Remarks:"requiredrequiredMessage="Really do not intend to write anything?"><Input.TextAreamaxLength={20}showLimitHintplaceholder="Everything is ok!"name="valTextarea"/></FormItem><FormItem wrapperCol={{ offset: 6 }}><Form.Submitvalidatetype="primary"onClick={(v, e) => console.log(v, e)}style={{ marginRight: 10 }}>Submit</Form.Submit><Form.Reset>Reset</Form.Reset></FormItem></Form>);}}ReactDOM.render(<BasicDemo />, mountNode);
使用 label 作为校验提示
import { Form, Input, Radio } from '@alifd/next';
const FormItem = Form.Item;const RadioGroup = Radio.Group;const formItemLayout = {labelCol: {span: 6},wrapperCol: {span: 14}};class Demo extends React.Component {render() {return (<Form {...formItemLayout} useLabelForErrorMessage colon><FormItem label="Account" required><Input placeholder="Input frank" name="valUsername" /></FormItem><FormItemlabel="Email"requiredrequiredTrigger="onBlur"format="email"><Inputplaceholder="Both trigget onBlur and onChange"name="valEmail"/></FormItem><FormItemlabel="Password"hasFeedbackrequiredrequiredMessage="Please enter password"><Input htmlType="password" name="valPasswd" /></FormItem><FormItemlabel="Gender"hasFeedbackrequiredrequiredMessage="Please select your gender"><RadioGroup name="valSex"><Radio value="male">Male</Radio><Radio value="female">Female</Radio></RadioGroup></FormItem><FormItemlabel="Remarks"requiredrequiredMessage="Really do not intend to write anything?"><Input.TextAreamaxLength={20}showLimitHintplaceholder="Everything is ok!"name="valTextarea"/></FormItem><FormItem wrapperCol={{ offset: 6 }}><Form.Submitvalidatetype="primary"onClick={(v, e) => console.log(v, e)}style={{ marginRight: 10 }}>Submit</Form.Submit><Form.Reset>Reset</Form.Reset></FormItem></Form>);}}ReactDOM.render(<Demo />, mountNode);
配合 Field
可以实现较复杂功能
import { Form, Input, Radio, Field, Button } from '@alifd/next';
const FormItem = Form.Item;const RadioGroup = Radio.Group;const formItemLayout = {labelCol: {span: 6},wrapperCol: {span: 14}};class BasicDemo extends React.Component {constructor(...args) {super(...args);this.field = new Field(this);this.validate = () => {this.field.validate(["sex"]);};}userExists(rule, value) {return new Promise((resolve, reject) => {if (!value) {resolve();} else {setTimeout(() => {if (value === "frank") {reject([new Error("Sorry, this username is already occupied.")]);} else {resolve();}}, 500);}});}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")) {return callback("Inconsistent password input twice!");} else {return callback();}}render() {const { getState, getValue, getError } = this.field;return (<Form {...formItemLayout} field={this.field}><FormItemlabel="Username:"hasFeedbackrequiredvalidator={this.userExists.bind(this)}help={getState("username") === "loading"? "Checking ...": getError("username")}><Input placeholder="Input frank" name="username" /><p>Hello {getValue("username")}</p></FormItem><FormItemlabel="Password:"hasFeedbackrequiredrequiredMessage="Please enter password"validator={this.checkPass.bind(this)}><Input htmlType="password" name="passwd" /></FormItem><FormItemlabel="Check your password:"hasFeedbackrequiredrequiredMessage="Enter your password again"validator={this.checkPass2.bind(this)}><InputhtmlType="password"placeholder="Enter the same password twice"name="rePasswd"/></FormItem><FormItemlabel="Gender:"hasFeedbackrequiredrequiredMessage="Please select your gender"><RadioGroup name="sex"><Radio value="male">Male</Radio><Radio value="female">Female</Radio></RadioGroup></FormItem><FormItem wrapperCol={{ offset: 6 }}><Button onClick={this.validate}>Validate by Field</Button><Form.Submitvalidatetype="primary"onClick={(v, e) => console.log(v, e)}style={{ margin: "0 10px" }}>Submit</Form.Submit><Form.Reset>Reset</Form.Reset></FormItem></Form>);}}ReactDOM.render(<BasicDemo />, mountNode);
展示和表单相关的其他组件。
import { Form, Input, Button, Checkbox, Radio, Select, Range, Balloon, DatePicker, TimePicker, NumberPicker, Field, Switch, Upload, Grid } from '@alifd/next';
const FormItem = Form.Item;const Option = Select.Option;const RangePicker = DatePicker.RangePicker;const { Row, Col } = Grid;const formItemLayout = {labelCol: { span: 6 },wrapperCol: { span: 14 }};class Demo extends React.Component {constructor(...args) {super(...args);this.field = new Field(this);}handleSubmit(value) {console.log("Form values:", value);}render() {const init = this.field.init;return (<Form {...formItemLayout} field={this.field}><FormItem label="I'm the title:"><p className="next-form-text-align">The quick brown fox jumps over the lazy dog.</p><p><a href="#">Link</a></p></FormItem><FormItem label="Password:"><Balloontrigger={<Input htmlType="password" {...init("pass")} />}align="r"closable={false}triggerType="hover">input password</Balloon></FormItem><FormItem label="NumberPicker:"><NumberPicker min={1} max={10} name="numberPicker" defaultValue={3} /><span>Something in here</span></FormItem><FormItem label="Switch:" required><Switch name="switch" defaultChecked /></FormItem><FormItem label="Range:" required><RangedefaultValue={30}scales={[0, 100]}marks={[0, 100]}name="range"/></FormItem><FormItem label="Select:" required><Select style={{ width: 200 }} name="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, marginBottom: 0 }}><DatePicker name="startDate" /></FormItem><FormItem style={{ marginBottom: 0 }}><DatePicker name="endDate" /></FormItem></Row></FormItem><FormItem label="RangePicker:" labelCol={{ span: 6 }} required><RangePicker name="rangeDate" /></FormItem><FormItem label="TimePicker:" required><TimePicker name="time" /></FormItem><FormItem label="Checkbox:"><Checkbox.Group name="checkbox"><Checkbox value="a">option 1 </Checkbox><Checkbox value="b">option 2 </Checkbox><Checkbox disabled value="c">option 3(disabled)</Checkbox></Checkbox.Group></FormItem><FormItem label="Radio:"><Radio.Group name="radio"><Radio value="apple">apple</Radio><Radio value="banana">banana</Radio><Radio disabled value="cherry">cherry(disabled)</Radio></Radio.Group></FormItem><FormItem label="Logo:"><Upload action="/upload.do" listType="text" name="upload"><Button type="primary" style={{ margin: "0 0 10px" }}>Upload</Button></Upload></FormItem><Row style={{ marginTop: 24 }}><Col offset="6"><Form.Submit type="primary" onClick={this.handleSubmit.bind(this)}>Submit</Form.Submit></Col></Row></Form>);}}ReactDOM.render(<Demo />, mountNode);
可以通过Form切换表单元素的预览态,切换前后布局结构相同
import { Form, Input, Switch, Rating, Field, Icon, Radio, Range, Checkbox, NumberPicker, Select, Upload } from '@alifd/next';
const FormItem = Form.Item;const formItemLayout = {labelCol: {span: 7},wrapperCol: {span: 16}};const fileList = [{uid: "0",name: "IMG.png",state: "done",url: "https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg",downloadURL:"https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg",imgURL:"https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg"},{uid: "1",name: "IMG.png",percent: 50,state: "uploading",url: "https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg",downloadURL:"https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg",imgURL:"https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg"},{uid: "2",name: "IMG.png",state: "error",url: "https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg",downloadURL:"https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg",imgURL:"https://img.alicdn.com/tps/TB19O79MVXXXXcZXVXXXXXXXXXX-1024-1024.jpg"}];class Demo extends React.Component {constructor(...args) {super(...args);this.state = {preview: false};this.submitHandler = e => {console.log(e);};this.onPreviewChange = checked => {this.setState({preview: checked});};this.ratingPreview = value => {return (<p>{value} {value > 2.5 ? <Icon type="smile" /> : <Icon type="cry" />}</p>);};}render() {return (<div><Form{...formItemLayout}isPreview={this.state.preview}style={{ maxWidth: "800px" }}><FormItemlabel="preview: "isPreview={false}size="small"style={{ marginBottom: 0 }}><Switch size="large" onChange={this.onPreviewChange} /></FormItem><div style={{ height: 1, width: "100%", margin: "20px 0" }} /><FormItem required label="Username:"><InputdefaultValue="Fusion"placeholder="Please enter your username"id="username"name="username"aria-required="true"/></FormItem><FormItem required label="Password:"><InputdefaultValue="Fusion@2019"htmlType="password"placeholder="Please enter your password"id="password"name="password"aria-required="true"/></FormItem><FormItem required label="Link:"><Inputname="link"addonTextBefore="http://"addonTextAfter=".com"defaultValue="alibaba"aria-label="input with config of addonTextBefore and addonTextAfter"/></FormItem><FormItem required label="Number:"><NumberPicker name="number" defaultValue={1} /></FormItem><FormItem required label="autoComplete:"><Select.AutoComplete name="autoComplete" defaultValue="selected" /></FormItem><FormItem required label="multiple Select:"><Selectname="select"defaultValue={["apple", "banana"]}mode="multiple"><Select.Option value="apple">Apple</Select.Option><Select.Option value="banana">Banana</Select.Option></Select></FormItem><FormItem required label="Rating:"><RatingdefaultValue={4.5}name="rate"aria-label="what's the rate score"/></FormItem><FormItemrequiredlabel="Custom Render Rating:"renderPreview={this.ratingPreview}><RatingdefaultValue={4.5}name="rate2"aria-label="what's the rate2 score"/></FormItem><FormItem required label="Checkbox:"><Checkbox.Group name="checkbox" defaultValue={["react", "vue"]}><Checkbox value="react">React</Checkbox><Checkbox value="vue">Vue</Checkbox><Checkbox value="angular">Angular</Checkbox></Checkbox.Group></FormItem><FormItem required label="Radio:"><Radio.Group name="radio" defaultValue={"react"}><Radio value="react">React</Radio><Radio value="vue">Vue</Radio><Radio value="angular">Angular</Radio></Radio.Group></FormItem><FormItem required label="Range:"><Range name="range" slider="double" defaultValue={[10, 80]} /></FormItem><FormItem label="Note:"><Input.TextAreaplaceholder="description"name="a11yRemark"defaultValue="Fusion 是一套企业级中后台UI的解决方案,致力于解决设计师与前端在产品体验一致性、工作协同、开发效率方面的问题。通过协助业务线构建设计系统,提供系统化工具协助设计师前端使用设计系统,下游提供一站式设计项目协作平台;打通互联网产品从设计到开发的工作流。"/></FormItem><FormItem label="Upload:"><Upload name="upload" defaultValue={fileList} listType="text" /></FormItem><FormItem label="Upload:"><Upload name="upload2" defaultValue={fileList} listType="image" /></FormItem><FormItem wrapperCol={{ offset: 7 }}><Form.Submit validate type="primary" onClick={this.submitHandler}>Submit</Form.Submit><Form.Reset style={{ marginLeft: 10 }}>Reset</Form.Reset></FormItem></Form></div>);}}ReactDOM.render(<Demo />, mountNode);
device=phone 下会强制设置 labelAlign=top
import { Form, Input, Checkbox, Switch, Radio } from '@alifd/next';
const FormItem = Form.Item;const formItemLayout = {labelCol: {fixedSpan: 10},wrapperCol: {span: 14}};class Demo extends React.Component {constructor(...args) {super(...args);this.state = {device: "desktop"};this.handleDeviceChange = device => {this.setState({device});};}render() {return (<div><Radio.Groupshape="button"value={this.state.device}onChange={this.handleDeviceChange}><Radio value="desktop">desktop</Radio><Radio value="phone">phone</Radio></Radio.Group><hr /><Formstyle={{ width: "60%" }}{...formItemLayout}device={this.state.device}><FormItem label="Username:"><p>Fixed Name</p></FormItem><FormItem label="password:"><InputhtmlType="password"name="basePass"placeholder="Please Enter Password"/></FormItem><FormItem label="Note:" help="something"><Input.TextArea placeholder="something" name="baseRemark" /></FormItem><FormItem label="Agreement:"><Checkbox name="baseAgreement" defaultChecked>Agree</Checkbox></FormItem><FormItem label=" "><Form.Submit>Confirm</Form.Submit></FormItem></Form></div>);}}ReactDOM.render(<Demo />, mountNode);
对于必填项,在组件中要设置aria-required
属性,并通过视觉设计上的高亮提示用户。
import { Form, Input, Select, Radio, Checkbox, DatePicker, Switch, Upload, Grid, Field } from '@alifd/next';
const RadioGroup = Radio.Group;const { Row, Col } = Grid;const FormItem = Form.Item;const Option = Select.Option;const formItemLayout = {labelCol: {span: 7},wrapperCol: {span: 16}};class Demo extends React.Component {constructor(...args) {super(...args);this.state = {size: "medium"};this.submitHandle = e => {console.log(e);};}render() {return (<div><Form{...formItemLayout}size={this.state.size}style={{ maxWidth: "800px" }}><FormItem required label="username:"><Inputplaceholder="Please enter your username"id="a11yUsername"name="a11yUsername"aria-required="true"/></FormItem><FormItem required label="Password:"><InputhtmlType="password"placeholder="Please enter your password"id="a11yPassword"name="a11yPassword"aria-required="true"/></FormItem><FormItemid="myDateInput-1"requiredlabel="Accessible Date 1 (YYYY/MM/DD):"requiredMessage="Please select your date"><DatePickername="a11yDate"format="YYYY/MM/DD"inputProps={{ "aria-required": "true", id: "myDateInput-1" }}/></FormItem><FormItemrequiredlabel="Accessible Date 2 (YYYY/MM/DD):"requiredMessage="Please select your date"><DatePickername="a11yOtherDate"format="YYYY/MM/DD"dateInputAriaLabel="Date input format YYYY/MM/DD"inputProps={{"aria-required": "true","aria-label": "Accessible Date 2"}}/></FormItem><FormItem label="Switch:"><Switchname="a11ySwitch"aria-label="Accessible Switch"defaultChecked/></FormItem><FormItemrequiredlabel="gender:"requiredMessage="Please select your gender"><RadioGroup name="a11ySex"><Radio value="male" aria-required="true">Male</Radio><Radio value="female" aria-required="true">Female</Radio></RadioGroup></FormItem><FormItem label="Language:"><Checkbox.Groupname="a11yLangs"aria-label="Please select a programming language"><Checkbox value="python">python</Checkbox><Checkbox value="java">java</Checkbox><Checkbox value="angular">angular</Checkbox><Checkbox value="c">c</Checkbox><Checkbox value="other">other</Checkbox></Checkbox.Group></FormItem><FormItem label="upload:"><Upload.CardlistType="card"action="https://www.easy-mock.com/mock/5b713974309d0d7d107a74a3/alifd/upload"accept="image/png, image/jpg, image/jpeg, image/gif, image/bmp"defaultValue={[]}limit={2}name="a11yUpload"/></FormItem><FormItem label="Note:"><Input.TextArea placeholder="description" name="a11yRemark" /></FormItem><FormItem wrapperCol={{ offset: 7 }}><Form.Submitvalidatetype="primary"onClick={this.submitHandle}style={{ marginRight: 10 }}>Submit</Form.Submit><Form.Reset>Reset</Form.Reset></FormItem></Form></div>);}}ReactDOM.render(<Demo />, mountNode);
参数 | 说明 | 类型 | 默认值 | 版本支持 |
---|---|---|---|---|
inline | 内联表单 | Boolean | - | |
size | 单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。 可选值: 'large'(大) 'medium'(中) 'small'(小) |
Enum | 'medium' | |
fullWidth | 单个 Item 中表单类组件宽度是否是100% | Boolean | - | |
labelAlign | 标签的位置, 如果不设置 labelCol 和 wrapperCol 那么默认是标签在上 可选值: 'top'(上) 'left'(左) 'inset'(内) |
Enum | 'left' | |
labelTextAlign | 标签的左右对齐方式 可选值: 'left'(左) 'right'(右) |
Enum | - | |
field | field 实例, 传 false 会禁用 field | any | - | |
saveField | 保存 Form 自动生成的 field 对象 签名: Function() => void |
Function | func.noop | |
labelCol | 控制第一级 Item 的 labelCol | Object | - | |
wrapperCol | 控制第一级 Item 的 wrapperCol | Object | - | |
onSubmit | form内有 htmlType="submit" 的元素的时候会触发签名: Function() => void |
Function | function preventDefault(e) { e.preventDefault(); } | |
children | 子元素 | any | - | |
value | 表单数值 | Object | - | |
onChange | 表单变化回调 签名: Function(values: Object, item: Object) => void 参数: values: {Object} 表单数据 item: {Object} 详细 item.name: {String} 变化的组件名 item.value: {String} 变化的数据 item.field: {Object} field 实例 |
Function | func.noop | |
component | 设置标签类型 | String/Function | 'form' | |
device | 预设屏幕宽度 可选值: 'phone', 'tablet', 'desktop' |
Enum | 'desktop' | |
responsive | 是否开启内置的响应式布局 (使用ResponsiveGrid) | Boolean | - | 1.19 |
isPreview | 是否开启预览态 | Boolean | - | 1.19 |
useLabelForErrorMessage | 是否使用 label 替换校验信息的 name 字段 | Boolean | - | 1.20 |
colon | 表示是否显示 label 后面的冒号 | Boolean | false |
手动传递了 wrapCol labelCol 会使用 Grid 辅助布局; labelAlign='top' 会强制禁用 Grid
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | label 标签的文本 | ReactNode | - |
size | 单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。 可选值: 'large', 'small', 'medium' |
Enum | - |
labelCol | label 标签布局,通 <Col> 组件,设置 span offset 值,如 {span: 8, offset: 16},该项仅在垂直表单有效 |
Object | - |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | Object | - |
help | 自定义提示信息,如不设置,则会根据校验规则自动生成. | ReactNode | - |
extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 位于错误信息后面 | ReactNode | - |
validateState | 校验状态,如不设置,则会根据校验规则自动生成 可选值: 'error'(失败) 'success'(成功) 'loading'(校验中) 'warning'(警告) |
Enum | - |
hasFeedback | 配合 validateState 属性使用,是否展示 success/loading 的校验状态图标, 目前只有Input支持 | Boolean | false |
children | node 或者 function(values) | ReactNode/Function | - |
fullWidth | 单个 Item 中表单类组件宽度是否是100% | Boolean | - |
labelAlign | 标签的位置, 如果不设置 labelCol 和 wrapperCol 那么默认是标签在上 可选值: 'top'(上) 'left'(左) 'inset'(内) |
Enum | - |
labelTextAlign | 标签的左右对齐方式 可选值: 'left'(左) 'right'(右) |
Enum | - |
required | [表单校验] 不能为空 | Boolean | - |
asterisk | required 的星号是否显示 | Boolean | - |
requiredMessage | required 自定义错误信息 | String | - |
requiredTrigger | required 自定义触发方式 | String/Array | - |
min | [表单校验] 最小值 | Number | - |
max | [表单校验] 最大值 | Number | - |
minmaxMessage | min/max 自定义错误信息 | String | - |
minmaxTrigger | min/max 自定义触发方式 | String/Array | - |
minLength | [表单校验] 字符串最小长度 / 数组最小个数 | Number | - |
maxLength | [表单校验] 字符串最大长度 / 数组最大个数 | Number | - |
minmaxLengthMessage | minLength/maxLength 自定义错误信息 | String | - |
minmaxLengthTrigger | minLength/maxLength 自定义触发方式 | String/Array | - |
length | [表单校验] 字符串精确长度 / 数组精确个数 | Number | - |
lengthMessage | length 自定义错误信息 | String | - |
lengthTrigger | length 自定义触发方式 | String/Array | - |
pattern | 正则校验 | any | - |
patternMessage | pattern 自定义错误信息 | String | - |
patternTrigger | pattern 自定义触发方式 | String/Array | - |
format | [表单校验] 四种常用的 pattern 可选值: 'number', 'email', 'url', 'tel' |
Enum | - |
formatMessage | format 自定义错误信息 | String | - |
formatTrigger | format 自定义触发方式 | String/Array | - |
validator | [表单校验] 自定义校验函数 签名: Function() => void |
Function | - |
validatorTrigger | validator 自定义触发方式 | String/Array | - |
autoValidate | 是否修改数据时自动触发校验 | Boolean | - |
device | 预设屏幕宽度 可选值: 'phone', 'tablet', 'desktop' |
Enum | - |
colSpan | 在响应式布局模式下,表单项占多少列 | Number | - |
labelWidth | 在响应式布局下,且label在左边时,label的宽度是多少 | String/Number | 100 |
isPreview | 是否开启预览态 | Boolean | - |
renderPreview | 预览态模式下渲染的内容 签名: Function(value: any) => void 参数: value: {any} 根据包裹的组件的 value 类型而决定 |
Function | - |
useLabelForErrorMessage | 是否使用 label 替换校验信息的 name 字段 | Boolean | - |
colon | 表示是否显示 label 后面的冒号 | Boolean | - |
valueName | 子元素的 value 名称 | String | - |
继承 Button API
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
onClick | 点击提交后触发 签名: Function(value: Object, errors: Object, field: class) => void 参数: value: {Object} 数据 errors: {Object} 错误数据 field: {class} 实例 |
Function | func.noop |
validate | 是否校验/需要校验的 name 数组 | Boolean/Array | - |
field | 自定义 field (在 Form 内不需要设置) | Object | - |
继承 Button API
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
names | 自定义重置的字段 | Array | - |
onClick | 点击提交后触发 签名: Function() => void |
Function | func.noop |
toDefault | 返回默认值 | Boolean | - |
field | 自定义 field (在 Form 内不需要设置) | Object | - |
自定义错误展示
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
name | 表单名 | String/Array | - |
field | 自定义 field (在 Form 内不需要设置) | Object | - |
children | 自定义错误渲染, 可以是 node 或者 function(errors, state) | ReactNode/Function | - |