Input · Form
Form

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, SectionTagInput All mounted under Form and declared directly in \<Form.Input> and \<Form.Select> when used.
  • Upload is already planned and will be supported in the follow-up
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
Notice
The field attribute is required props

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

Via children function

Children is a function that returns all form controls

Via props.component

Pass the entire internal structure directly in the form through component attribute.

Supported Fields example collection

Field binding syntax

Every Field must have a field property. This is how the form manages the state of this field. See the field syntax section below for additional details on what you can pass in for field.
The field can be a simple string, can be contained.Or[]String that supports multi-level nesting
Below is an example of the field name and their mapping path in FormState
FieldResolution
usernameformState.values.username
user[0]formState.values.user[0]
siblings.1formState.values.siblings[1]
siblings['2']formState.values.siblings[2]
parents[0].nameformState.values.parents[0].name
parents[1]['name']formState.values.parents[1].name

Form layout

  • Vertical Layout: Arrange each field vertically (By default)
    Semi Design recommends a vertical layout.
  • Horizontal Layout: Arrange each field horizontally You can use the horizontal layout by setting layout='layout'
  • 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
  • A more complex layout.
    You can also combine the Row and Col provided by the Grid to arrange the form structure as you want.

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,labelColProperty Configuration Reference Col components

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

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 / ErrorMessage

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

Embedded Label

By setting the labelPositon toinset, you can embed label in the field component. This feature currently support Input, InputNumber, DatePicker, TimePicker, Select, Cascader, TreeSelect

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.InputForm.Select, Form will insert Label module automatically for them.
But usually, inInputGroup 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 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

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
  • 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)

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)

Asynchronous Validate

For asynchronous validation, you should return a promise. In promise.then() you need to return the corresponding error message.

Custom Validate (Field Level)

You can specify a custom validation function for field. Supports synchronous and asynchronous validation (by returning promises)

Linkage Fields

You can achieve the linkage between Fields by listening to the onChange of Field and then using formApi to make modifications.

Dynamic form

Dynamically add and delete fields

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

Add or delete form items dynamically - by use formApi

If you don't use ArrayField, you can use the provided formApi to manually add or delete formState.

Use of Hook

We provide four Hooks so that you can easily access Form internal state and call Form and Field related api in Functional Component which placed inside the Form structure without passing through props.

useFormApi

useFormApi allows you to directly access the formApi of the parent Form component within Functional Component via hook

useFormState

useFormState allows you to directly access the form State of the parent Form component within Functional Component via hook

useFieldApi

useFieldApi allows you to call the api of the specified Field directly within Functional Component via hook

useFieldState

useFieldState allows you to directly access the State of the specified Field within Functional Component via hook

Use of HOC

We provided two HOC: withFormApiwithFormState, you can access the API of the Form and the internal state within other components
Provided HOC: withField, to encapsulating custom components as Field that conform the Semi Form data flow.

HOC - withFormApi

You can encapsulate the component via withFormApi HOC so that the formApi of the parent Form component can be called directly inside the component
Note that the encapsulated components must be placed inside the Form structure.

HOC - withFormState

You can encapsulate the component via withFormState HOC so that the component has direct access to the Form State of the parent Form component.
Note that the encapsulated components must be placed inside the Form structure.

With Field encapsulation custom form control

Via withField, you can extend other custom components into Field. Form will taking over its behavior.
Note: Custom components must be controlled components.
With Field did the following things.
  • Take over the value of the component (or other properties specified by valueKey), onChange (or other callback functions specified by onKeyChangeFnName)
  • Insert Field's <Form.Label>above the field
  • Insert Field's <ErrorMessage> under the field
With Field Options specific configuration can be consulted withFieldOption

API reference

Form Props

PropertiesInstructionsTypeDefault
autoScrollToErrorIf setting true,when submit or call formApi.validate () fails verification, it will automatically scroll to the wrong field, object config refer to optionsboolean| objectfalse
allowEmptyWhether to keep the key of the null field in the values, keep the key when true, and remove the key when falsebooleanfalse
componentFor declaring fields, not used at the same time as render, props.childrenReactNode
classNameClassname for form tagstring
disabledIf true, all fields inside the form structure will automatically inherit the disabled attributebooleanfalse
extraTextPositionThe extraTextPosition property applied to each Field uniformly controls the display position of extraText. Middle (the vertical direction is displayed in the order of Label, extraText, and Field), bottom (the vertical direction is displayed in the order of Label, Field, and extraText)
since v1.9.0
string'bottom'
getFormApiThis function will be executed once when the form is mounted and returns formApi.
formApi can be used to modify the internal state of the form (value, touched, error)
function (formApi: object)
initValuesUsed to uniformly set the initial value of the form
(will be consumed only once when form is mount)
object
layoutThe layout of fields, optional horizontal or verticalstring'vertical'
labelColUniformly applied to the label label layout of each Field, with Col Component,
set span, span values, such as {span: 6, selected: 2}
object
labelAlignText-align value of labelstring'left'
labelPositionLocation of label in Field, optional 'top', 'left', 'inset'
(inset label only partial component support)
string'top'
labelWidthWidth of field'r labelstring|number
onChangeCallback invoked when form update, including Fields mount/unmount / value change /
blur / validation status change / error status change.
function (formState: object)
onValueChangeCallback invoked when form values updatefunction (values: object, changedValue: object)
onResetCallback invoked after clicked on reset button or executed formApi.reset()function ()
onSubmitCallback invoked after clicked on submit button or executed formApi.submit(),
and all validation pass.
function (values: object)
onSubmitFailCallback invoked after clicked on submit button or executed formApi.submit(),
but validate failed.
function (object, values: object)
renderFor declaring fields, not used at the same time as component, props.childrenfunction
showValidateIconWhether the verification information block in the field automatically adds the corresponding status icon display
since v1.0.0
booleantrue
validateFieldsForm-level custom validate functions are called at submit or formApi.validate().
Supported synchronous / asynchronous function
function (values)
wrapperColUniformly apply the layout on each Field, with Col component,
set span, span values, such as {span: 20, selected: 4}
object

FormState

FormState stores all the state values within the Form, including the values of each field, error information, touched status
NameInstructionsInitial valueExample
valuesValue Collection of the form{}{fieldA: 'str', fieldB: true}
errorsForm error information collection, you can decide whether to allow users to submit by judging whether there is error information{}{fieldA: 'length not valid'}
touchedThe collection of fields the user has clicked on{}{fieldA: true}

How to access the form state

  • By calling formApi.getFormState()
  • By declaring fields through "child render function", formState will injected as a parameter
  • By declaring fields through "render props", formState will injected as a parameter
  • Via useFormState hook
  • Via withFormState HOC

FormApi

We provide FormApi. You have easy access to FormApi both inside and outside the Form, which allows you to use getter and setter to get and manipulate the values of FormState.
The table below describes the features available in the formApi.
FunctionDescriptionexample
getFormStateGet FormStateformApi.getFormState()
submitFormmanually submit the submit operationformApi.submitForm()
resetreset the form manuallyformApi.reset()
validateManually trigger validation of the entire formformApi.validate()
.then(values ​​=> {})
.catch(errors => {})
setValues ​​Set the values ​​of the entire form. The isOverride in the second parameter is false by default.
By default, only the values ​​of the existing field in the Form are updated from newValues toformState.values.
When isOverride is true, the newValues ​​will be overwritten and assigned to formState.values ​​
formApi.setValues(newValues: object, {isOverride: boolean})
getValues ​​Get the values of all FieldformApi.getValues()
setValueprovides direct modification of formState.values ​​method.
The difference from setValues ​​is that it only modifies a single field.
formApi.setValue(field: string, newFieldValue: any)
getValueGet the value of all / single FieldformApi.getValue()
formApi.getValue(field: string)
setTouchedmodify formState.touchedformApi.setTouched(field: string, isTouched: boolean)
getTouchedGet the touched state of the FieldformApi.getTouched(field: string)
setErrorModify the error information of a fieldformApi.setError(field: string, fieldErrorMessage: string)
getErrorGet Error Status of FieldformApi.getError(field: string)
getFieldExistGet whether the field exists in the FormformApi.getFieldExist(field: string)
scrollToFieldScroll to fieldformApi.scrollToField(field: string, scrollOpts: object)

How to access formApi

  • The Form component in the ComponentDidMount phase will execute the getFormApi callback passed in by props. You can save a reference to formApi in the callback function for subsequent calls (example code below) In addition, we provide other ways to get formApi, and you can choose different ways of calling according to your preference.
  • Use reference to get form instance,you can access form instance & its formApi
  • By declaring fields through "child render function", formApi will injected as a parameter
  • By declaring fields through "render props", formApi will injected as a parameter
  • Via useFormApi hook
  • Via "withFormApi" HOC

Field Props

About Field ref
Versions before v1.30.0, the Field component will not do ref forwarding
After v1.30, the underlying component instance can be obtained directly through ref, such as specifying ref to Form.Input and Form.Select, and directly obtaining the ref reference of the underlying original Input and Select components
PropertiesDescriptionTypeDefaultExamples
fieldThe mapping path of the field's value in formState.values. Form will use this value to distinguish the internal form control.
Required!!!
string
labelThe label text for this field. When not passed, it defaults to the same name as fieldstring
labelPositionLabel position of this field, optional 'top' / 'left' / 'inset'string
labelAlignText-align of the label text of this fieldstring
labelWidthThe width of the label text of this fieldstring|number
noLabelWhen you don't need to add label automatically, you can set this value to trueboolean
nameField name. When passed in, the corresponding className will be automatically added to the corresponding field div, such as: money => '.semi-form-field-money'string
fieldClassNameThe className of the entire fieldWrapper is the same as the name parameter, except that the prefix is ​​not automatically appendedstring
fieldStyleThe inline style of the entire fieldWrapper
since v1.9.0
object
initValueThe initial value of the field (consumed only once when Field mounted, subsequent updates are invalid), it has higher priority than the values ​​in Form's initValues ​​any(type depends on current component)
validateThe custom validation function for this form control. Supports synchronous and asynchronous verification.
Rules does not take effect when validate is set
function(fieldValue, values)(fieldValue) => fieldValue.length>5? 'error balabala': ''
rulesvalidation rules, validation library based on async-validatorarrayconst rules = [{type:' string ', message:' invalidate string'} ]
validateStatusThe validation result status of this form control, optional: success / error / warning / defaultstring'default'
triggerThe timing of triggering the verification, optional: blur / change / custom / mount
1. When set to custom, only formApi will trigger the verification
2。mount (triggered once when mounting)
string'change'
onChangeCallback invoked when this field value changes
transformtransform field values before validationfunction(fieldValue)(value) => Number(value)
allowEmptyStringWhether to allow values to be empty strings.
When the value is '' by default, the key corresponding to this field will be removed from values.
If you want to keep the key, you need to set allowEmptyString to true
booleanfalse
convertAfter the field value changes, before rerender, update the value of filedfunction(fieldValue)(value) => newValue(value)
stopValidateWithErrorWhen it is true, the rules check is used. After encountering the first rule that fails the check, it will no longer trigger the check of subsequent rules
since v0.35.0
booleanfalse
helpTextCustom prompt information, which is displayed in the same block as the verification information. When both have values, the verification information is displayed first
since v1.0.0
ReactNode
extraTextAdditional prompt information, you can use this when both error information and prompt copy are required, after helpText/errorMessage
since v1.0.0
ReactNode
pureWhether to only take over the data stream, when true, it will not automatically insert modules such as ErrorMessage, Label, extraText, etc. The style and DOM structure are consistent with the original components
since v1.1.0
booleanfalse
extraTextPositioncontrols the display position of extraText. Middle (the vertical direction is displayed in the order of Label, extraText, and Field), bottom (the vertical direction is displayed in the order of Label, Field, and extraText)
since v1.9.0
string'bottom'
...otherThe other configurable properties of the component can be passed in together with the above properties, such as the size / placeholder of Input,Field passes it to the component itself

Field Api

We also provide fieldApi, most of which is similar to formApi, with the difference that fieldApi limits the scope of modification, and it can only modify the bound field
FunctionInstructionsexample
setValueModify the value of the current FieldfieldApi.setValue(newValue: any)
getValueGets the value of the current FieldfieldApi.getValue()
setTouchedModify the value of the current FieldfieldApi.setTouched(true)
getTouchedGet Field's statusfieldApi.getTouched()
setErrorModify the error information of the current FieldfieldApi.setError(newErrorMessage: string)
getErrorGets field's error statusfieldApi.getError()

Form.Section

Form.Section is available since v1.0.0
PropertiesInstructionsType
textTitle of sectionReactNode
classNameClassnamestring
styleInline styleobject
childrenContent of sectionReactNode

Form.Label

By default, Label is self-inserted into each Field by Form.
If you need to self-insert Label elsewhere, we have provided the Label component for you.
PropertiesInstructionsTypeDefault
textLabel contentReactNode
requiredWhether to show the required *booleanfalse
extraContent after requiredReactNode
alignText-alignstring'left'
classNameClassname of label wrapperstring
styleInline stylestring
widthLabel widthnumber
optionalWhether to automatically append the "(optional)" text mark after the text (automatically switch the same semantic text according to different languages configured by Locale). When this item is true, the required * will no longer be displayed.booleanfalse

Form.Slot

PropertiesInstructionsTypeDefault
labelSlot's Label configuration, for example {text: 'semi', align: 'left'}; can also be passed directly into string, inside the Slot will be automatically encapsulated in legal Label formatobject|string
classNameClassname of Slot Wrapperstring
styleSlot inline styleobject
childrenContent of slot. You can place your custom component hereReactNode

Form.ErrorMessage

  • When the error is React Node, String, boolean, render directly
  • When the error is an array, the join operation is automatically performed to aggregate the error information in the array
PropertiesInstructionsTypeDefault
errorError message contentstring|array|ReactNode|undefined|boolean{}
classNameClassname of ErrorMessage wrapperstring
styleInline styleobject

withFieldOption

keyDescriptionDefault
valueKeyThe component represents the property of the value, such as Switch, Radio is' checked 'and Input is' value ''value'
onKeyChangeFnNameThe callback function when the component value changes, generally 'onChange''onChange'
valuePathThe path of the value attribute to the first parameter in the callback function, such as Radio's onChange (e.target. checked), then the value needs to be set to target .checked; Radio Group's onChange (e.target. value), which is' target .value '; if the first parameter is the value itself, there is no need to take the value down, the item does not need to be set
withCursorDo you need to maintain a cursor for Input class componentsfalse

Accessibility

ARIA

  • aria-labelledby、for
    • Field component will automatically add label DOM. The for attribute of label is the same as props.id or props.name or props.field; the id attribute of label is determined by props.id or props.name or props.field, and the value format is ${props.field}-label;
    • When the props.labelPosition of the Form or Field is set to inset, there is no label tag at this time, but a div tag. The div tag corresponding to insetLabel will be automatically appended with id, the value is the same as the id of the above label, corresponding to the aria-labelledby of the Field component
    • The Field component will be automatically appended with aria-labelledby, the value is the same as the id of the above label
  • aria-required
    • When the Field is configured with required fields (that is, props.rules contains require: true or props.label is configured with required: true), the Field component will be automatically appended with aria-required = true (except Form.Switch, Form.CheckboxGroup)
  • aria-invalidaria-errormessage
    • When the Field check fails, the Field component will be automatically added with the aria-invalid = true attribute, except for Form.CheckboxGroup.
    • When the Field check fails, the Field component will be automatically appended with the aria-errormessage attribute, the value of which is the id of the DOM element corresponding to the errorMessage (format like: ${props.field}-errormessage), except for Form.CheckboxGroup.
  • aria-describedby
    • When the Field is configured with helpText or extraText, the Field component will be automatically added with the aria-describedby attribute, whose value is the id of the DOM element corresponding to helpText and extraText (format like: ${props.field}-helpText , ${props.field}-extraText)

Content Guidelines

  • Form title
    • The title of the form needs to follow the writing specification of the title
  • Form label
    • The label is a short description of the input box. The label is not a help text, so it should not be a description of the input
    • Labels must:
      • Place it above or below the input box
      • Short (1-3 words)
      • Use case conventions for statements (first letter uppercase, others lowercase)
  • Help text
    • Help text use statement writing conventions, capitalized
  • Form button

Design Tokens

FAQ

  • Why did I declare the form, modify the value, and the data is not automatically mapped to formState.values?
    Check that the field has been passed correctly, and the field attribute on the Field component is a must-fill property !
  • Why doesn't the passed defaultValue or defaultChecked take effect?
    Refer to the beginning of the document Field. The Form.Field component unifies the default value. You should pass the default value using initValue or initValues
  • Why did the component not change and the value not take effect after initValue and initValues were updated asynchronously?
    initValue, initValues are only consumed once when Field and Form mount, and subsequent asynchronous updates will not take effect.
    If your initial value needs to be taken remotely, you can update it using formApi.setValue / setValues after you get the value
    Or send a new key directly to Form or Field to force it to remount.
  • Why can't getValues get a certain field?
    If the field has no initial value, getValues cannot get this item. You can set initValues/initValue or set the allowEmpty attribute to the form.
  • Why does hitting enter on the input box trigger the form's submit?
    This is standard HTML behavior. We do not plan to intervene and we will remain the same as the HTML. If there is really only one input element in the form, and you don't want to trigger the submit callback when you press Enter, it is recommended to use preventDefault for the enter of the keydown event of input to prevent the default behavior.
    Click #767 for background and content.
  • The form will automatically save the historical input items, what should I do if I don't want this function?
    Before v2.3, Form did not configure for, name, id and other attributes for input controls strictly according to the A11y accessibility standard, so this function was not available in previous versions. After v2.3, we implemented it strictly according to the W3C standard. If you don't want the browser to automatically save history input items, you can also turn it off by setting autoComplete=off at the Form level or Field level

© 2021 - 2022 Semi Design. All rights reserved.

京ICP备19058139号-13浙公网安备 33011002016131号

Designed & Developed with love by Douyin FE & MED