--- localeCode: en-US order: 22 category: Input title: Form subTitle: Form icon: doc-form dir: column --- ## Form - **Rerender on demand**, avoids unnecessary full-volume rendering, higher performance - Easy to use, **simple structure**, avoids unnecessary hierarchical nesting - FormState / FieldState can also be easily obtained from outside the Form Provides an external method to operate inside the form: formApi / fieldApi - Support for encapsulating custom components into form controls, and you can quickly access your team's components through the extension mechanism provided by Form (through `withField` HOC) - Support Form level / Field level assignment, verification (synchronous / asynchronous) ## Field Semi encapsulates all form controls (Input、Select、Checkbox、DatePicker etc.) with withField once. Taking over their data flow (value & onChange) When in use, you need to import from the Form (note: only the control imported from the Form has data synchronization) ### Supported Field Component - `Input`, `InputNumber`, `TextArea`, `Select`, `Checkbox`, `Radio`, `RadioGroup`, `Switch`, `DatePicker`, `TimePicker`, `Slider`, `InputGroup`, `TreeSelect`, `Cascader`, `Rating`, `AutoComplete`, `Label`, `ErrorMessage`, `Section`、`TagInput` All mounted under Form and declared directly in \ and \ when used. - `Upload` is already planned and will be supported in the follow-up ```javascript import { Form } from '@douyinfe/semi-ui'; const FormInput = Form.Input; const FormSelect = Form.Select; const Option = FormSelect.Option; ``` The Field level component provided by Form, its `value` (or other properties specified by `valueKey`), onChange (or other callback functions specified by `onKeyChangeFnName`) Properties are hijacked by Form, so 1. ** You don't need and shouldn't use `onChange` to sync, of course you can continue to listen to onChange events for the latest values ** 2. ** You cannot set the `value` of field with attributes such as `value`, `defaultValue`, `checked`, `defaultChecked`, etc. The default value can be set by Field's `initValue` or Form's `unitValues` ** 3. ** You should not modify the value of Form State directly, all changes to the data in the Form should be done by providing `formApi`, `fieldApi` ** ## Demos ### Various ways to declare form Semi Form supports multiple writing at the same time. #### Basic Usage Add `field` property to each field component. You can also set label` properties for each field, by default is the same as field `label` can be passed in a string directly, or declared in the form of an object, configure `extra`, `required`, `optional` and other attributes to deal with more complex scenarios The field attribute is required props ```jsx live=true dir="column" import React from 'react'; import { Form, Tooltip } from '@douyinfe/semi-ui'; import { IconHelpCircle } from '@douyinfe/semi-icons'; () => (
}} style={{ width:176 }} /> ); ``` #### Other forms of support When you need to get formState, formApi, values, etc. directly inside the Form structure, you can also use the following writing #### Via render props ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
( <> Admin User Guest {JSON.stringify(formState)} )} layout='horizontal'> ); ``` #### Via children function Children is a function that returns all form controls ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
{ ({ formState, values, formApi }) => ( <> Admin User Guest {JSON.stringify(formState)} ) } ); ``` #### Via props.component Pass the entire internal structure directly in the form through `component` attribute. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class Demo extends React.Component { constructor() { super(); } render() { const fields = ({ formState, formApi, values }) => ( <> {JSON.stringify(formState)} ); return
; } } ``` ### Supported Fields example collection ```jsx live=true dir="column" import React from 'react'; import { Form, Col, Row, Button } from '@douyinfe/semi-ui'; class BasicDemoWithInit extends React.Component { constructor() { super(); this.state = { initValues: { name: 'semi', business: ['hotsoon'], role: 'ued', switch: true, } }; this.getFormApi = this.getFormApi.bind(this); } getFormApi(formApi) { this.formApi = formApi; } render() { const { Input, InputNumber, AutoComplete, Select, TreeSelect, Cascader, DatePicker, TimePicker, TextArea, CheckboxGroup, Checkbox, RadioGroup, Radio, Slider, Rating, Switch, TagInput } = Form; const { initValues } = this.state; const plainOptions = ['A', 'B', 'C']; const style = { width: '90%' }; const treeData = [ { label: 'Asia', value: 'Asia', key: '0', children: [ { label: 'China', value: 'China', key: '0-0', children: [ { label: 'Beijing', value: 'Beijing', key: '0-0-0', }, { label: 'Shanghai', value: 'Shanghai', key: '0-0-1', }, ], }, ], }, { label: 'North America', value: 'North America', key: '1', } ]; return ( console.log(v)} > ) }
); ``` ### Form layout - Vertical Layout: Arrange each field vertically (By default) Semi Design recommends a vertical layout. ```jsx live=true dir="column" import React from 'react'; import { Form, Button, Toast } from '@douyinfe/semi-ui'; () => { const handleSubmit = (values) => { console.log(values); Toast.info('Submit Success'); }; return (
handleSubmit(values)} style={{width: 400}}> {({formState, values, formApi}) => ( <> I have read and agree to the terms of service

Or

)}
); }; ``` - Horizontal Layout: Arrange each field horizontally You can use the horizontal layout by setting `layout='layout'` ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
); ``` - Label Position, Label Align You can control the position of the label in the Field and the direction of text alignment by setting `labelPosition`, `labelAlign` ```jsx live=true dir="column" import React from 'react'; import { Form, Select, Checkbox, Radio } from '@douyinfe/semi-ui'; class BasicDemo extends React.Component { constructor() { super(); this.state = { labelPosition: 'left', labelAlign: 'left', labelWidth: '180px' }; this.changeLabelPos = this.changeLabelPos.bind(this); this.changeLabelAlign = this.changeLabelAlign.bind(this); } changeLabelPos(labelPosition) { let labelWidth; labelPosition === 'left' ? labelWidth = '180px' : labelWidth = 'auto'; this.setState({ labelPosition, labelWidth }); } changeLabelAlign(labelAlign) { this.setState({ labelAlign }); } render() { const { labelPosition, labelAlign, labelWidth } = this.state; return ( <>
Switch Label Position: Switch Label Text Align
value === 'semi', message: 'not semi' } ]} /> admin user guest root man woman ); } } ``` - A more complex layout. You can also combine the `Row` and `Col` provided by the `Grid` to arrange the form structure as you want. ```jsx live=true dir="column" import React from 'react'; import { Form, Row, Col } from '@douyinfe/semi-ui'; () => (
value === 'semi', message: 'not semi' } ]} /> Semi Vigo BussVideo value === 'semi', message: 'not semi' } ]} /> Semi Vigo BussVideo Quality Assurance Software Engineer Product Manager Designer
); ``` ### wrapper Col / label Col When you need to set a uniform layout for all Fields in a Form, you can set `wrapperCol` and `labelCol` on the `Form` to quickly generate the layout. No need to manually use `Row`, `Col` manual layout. `wrapperCol`,`labelCol`Property Configuration Reference [Col components](/en-US/basic/grid#Col) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
Quality Assurance Software Engineer Product Manager Designer ); ``` ### Remove automatically added Label Form will automatically inserts `Label` for Field Component. If you do not need to automatically insert the `Label` module, you can turn off this feature by setting `noLabel=true` in Field ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
console.log(values)} style={{ width: 400 }}> Quality Assurance Software Engineer Product Manager Designer ); ``` ### Export Label, ErrorMessage use When the built-in Label and ErrorMessage layout does not meet the business requirements, you need to combine the positions yourself, but you want to use the default styles of Label and ErrorMessage directly. you can import them from the `Form` module, and combine `Form.Label` / `Form.ErrorMessage` by yourself. For details of their API, refer to [Label](#Form.Label) / [ErrorMessage](#Form.ErrorMessage) ```jsx import { Form } from '@douyinfe/semi-ui'; const { Label, ErrorMessage } = Form; ``` ### Use Form.Slot When your custom component needs to maintain the same layout style as the Field component, you can place your custom component in `Form.Slot` `labelWidth`, `labelAlign`, `wrapperCol`, `labelCol` set on the Form component automatically acts on `Form.Slot` For the Slot property configuration, refer to [Form.Slot](#Form.Slot) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class AssistComponent extends React.Component { constructor() { super(); } render() { return (
console.log(v)} onSubmit={v=>console.log(v)} style={{width: 600}} labelPosition='left' labelWidth={100} > FaceSticker BackgroundSticker
{`I'm Semi Form SlotA, a custom ReactNode`}
{`I'm Semi Form SlotA, i have different labelWidth and textAlign.`}
);} } ``` ### Embedded Label By setting the `labelPositon` to`inset`, you can embed label in the field component. This feature currently support `Input`, `InputNumber`, `DatePicker`, `TimePicker`, `Select`, `Cascader`, `TreeSelect` ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
Quality Assurance Software Engineer Product Manager Designer ); ``` ### Using Input Group When you need to combine some fields to use, you can use `Form.InputGroup` to wrap them. In Semi Form, when you using field components like `Form.Input`、`Form.Select`, Form will insert Label module automatically for them. But usually, in`InputGroup` you only need a Label belonging to the entire Group. You can set the label property in the `InputGroup` to insert a Label belonging to the Group `label` configurable properties, see [Label](#Form.Label) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; () => (
console.log(values)} labelPosition='top' style={{ width: 400 }}> PhoneNumber), required: true }} labelPosition='top'> USA +1 China +86 Japan+81
); ``` ### Form in the Modal pop-up layer You can place the Form in Modal and load it as a popup. When submitting, use `formApi.validate()` to centrally verify the Field ```jsx live=true dir="column" import React from 'react'; import { Form, Modal, Select, Button, Row, Col } from '@douyinfe/semi-ui'; class ModalFormDemo extends React.Component { constructor(props) { super(props); this.state = { visible: false, }; this.showDialog = this.showDialog.bind(this); this.handleOk = this.handleOk.bind(this); this.handleCancel = this.handleCancel.bind(this); this.getFormApi = this.getFormApi.bind(this); } showDialog() { this.setState({ visible: true }); } handleOk() { this.formApi.validate() .then((values) => { console.log(values); }) .catch((errors) => { console.log(errors); }); } handleCancel() { this.setState({ visible: false }); } getFormApi(formApi) { this.formApi = formApi; } render(){ const { visible } = this.state; let message = 'Required'; return ( <>
China USA Europe Japan
); } } ``` ### Configure initial values and verification rules - You can configure check rules for each Field through `rules` The verification library inside the Form is based on async-validator, and more configuration rules can be found in its [official documentation](https://github.com/yiminghe/async-validator) - You can uniformly set the initial value for the entire form through the `initValues` of form, or you can set the initial value through `initValue` in each field (the latter has a higher priority) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class BasicDemoWithInit extends React.Component { constructor() { super(); this.state = { initValues: { name: 'semi', role: 'rd' } }; this.getFormApi = this.getFormApi.bind(this); } getFormApi(formApi) { this.formApi = formApi; } render() { const { Select, Input } = Form; const style = { width: '100%' }; return (
value === 'muji', message: 'not muji' } ]} />
); } } ``` ### Custom Validate (Form Level) You can set a custom validation function validateFields for the `form` as a whole, which will be called when submit #### Synchronous Validate When validate success, you should return an empty string. When validate fails, you should return the error message (Object, key is fieldName, value is the corresponding error message) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FormLevelValidateSync extends React.Component { constructor() { super(); this.syncValidate = this.syncValidate.bind(this); } syncValidate(values) { const errors = {}; if (values.name !== 'mike') { errors.name = 'you must name mike'; } if (values.sex !== 'female') { errors.sex = 'must be woman'; } errors.familyName = [ { before: 'before errror balabala ', after: 'after error balabala' }, 'familyName[1] error balabala' ]; return errors; } render() { return (
); } } ``` #### Asynchronous Validate For asynchronous validation, you should return a promise. In promise.then() you need to return the corresponding error message. ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FormLevelValidateAsync extends React.Component { constructor() { super(); this.asyncValidate = this.asyncValidate.bind(this); } asyncValidate(values) { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); return sleep(2000).then(() => { let errors = {}; if (values.name !== 'mike') { errors.name = 'you must name mike'; } if (values.sex !== 'female') { errors.sex = 'sex not valid'; } return errors; }); } render() { return (
); } } ``` ### Custom Validate (Field Level) You can specify a custom validation function for field. Supports synchronous and asynchronous validation (by returning promises) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FieldLevelValidateDemo extends React.Component { constructor() { super(); this.validateName = this.validateName.bind(this); this.asyncValidate = this.asyncValidate.bind(this); } validateName(val) { if (!val) { return '【sync】can\'t be empty'; } else if (val.length <= 5) { return '【sync】must more than 5'; } return ''; } asyncValidate(val, values) { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); return sleep(2000).then(() => { if (!val) { return '【async】can\'t be empty'; } else if (val.length <= 5) { return '【async】must more than 5'; } else { return ''; } }); } render() { return (
); } } ``` ### Linkage Fields You can achieve the linkage between Fields by listening to the `onChange` of Field and then using formApi to make modifications. ```jsx live=true dir="column" import React from 'react'; import { Form, Button, Row } from '@douyinfe/semi-ui'; class LinkFieldForm extends React.Component { constructor() { super(); this.getFormApi = this.getFormApi.bind(this); this.handleSelectChange = this.handleSelectChange.bind(this); } handleSelectChange(value) { let text = value === 'male' ? 'Hi male' : 'Hi female!'; this.formApi.setValue('Note', text); } getFormApi(formApi) { this.formApi = formApi; } render() { return (
console.log(values) } style={{ width: 300 }}> female male ); } } ``` ### Dynamic form #### Dynamically add and delete fields ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; () => (
{({ formState }) => ( yes no {formState.values.isAnchor === 'yes' ? ( ) : null} )}
); ``` #### Add or delete form items dynamically - by use ArrayField For array items that are dynamically added or deleted, we provide the `ArrayField` component to simplify the operation of add / remove ```jsx live=true dir="column" import React from 'react'; import { ArrayField, TextArea, Button, Form, useFormState } from '@douyinfe/semi-ui'; class ArrayFieldDemo extends React.Component { constructor() { super(); this.state = { menu: [ { name: 'Face stickers', type: '2D' }, { name: 'Background sticker', type: '3D' }, ] }; } render() { let { menu } = this.state; const ComponentUsingFormState = () => { const formState = useFormState(); return (