JGuang

Form

Form · Ant Design Mobile

Preview
npm install antd-mobile@5.43.0

Form

High-performance form controls with built-in data field management. Including data entry, verification and corresponding styles.

Install

npm install antd-mobile@5.43.0

antd-mobile is for mobile web, not React Native or Mini Programs. React 19 projects must register createRoot with unstableSetRender at the entry, or imperative APIs such as Toast and Dialog cannot render. Switch dark mode with <html data-prefers-color-scheme="dark">.

Examples come from the official Ant Design Mobile repository (MIT) at the commit published as npm 5.43.0. Presentation helpers such as DemoBlock are JGuang rewrites, not antd-mobile APIs.

High-performance form controls with built-in data field management. Including data entry, verification and corresponding styles.

When to Use

  • Suitable for creating an entity or collecting information.
  • When the input data type needs to be checked.

Demos

Validate Only

You can use validateOnly of validateFields to validate the form without showing error UI. This is commonly used to dynamically control the disabled state of the submit button.

getvalueprops-normalize

Form

Props

NameDescriptionTypeDefault
disabledWhether it is disabledbooleanfalse
footerThe footer content. Commonly used for placing submit buttons.ReactNode-
formForm control instance created by Form.useForm(). Automatically created when not providedFormInstance-
hasFeedbackWhether to show error feedbackbooleantrue
initialValuesSet value by Form initialization or resetobject-
layoutLayout mode'vertical' | 'horizontal''vertical'
modeSupport two modes: default and card.'default' | 'card''default'
nameForm name. Will be the prefix of Field idstring-
onFieldsChangeTrigger when field updated(changedFields, allFields) => void-
onFinishTrigger after submitting the form and verifying data successfully(values) => void-
onFinishFailedTrigger after submitting the form and verifying data failed({ values, errorFields, outOfDate }) => void-
onValuesChangeTrigger when value updated(changedValues, allValues) => void-
preserveKeep field value even when field removedbooleantrue
requiredMarkStyleThe style of required or optional mark'asterisk' | 'text-required' | 'text-optional | 'none''asterisk'
validateMessagesValidation prompt template, description see belowValidateMessages-
validateTriggerConfig field validate triggerstring | string[]'onChange'

FormInstance

NameDescriptionType
getFieldErrorGet the error messages by the field name(name: NamePath) => string[]
getFieldValueGet the value by the field name(name: NamePath) => any
getFieldsErrorGet the error messages by the fields name. Return as an array(nameList?: NamePath[]) => FieldError[]
getFieldsValueGet values by a set of field names. Return according to the corresponding structure. Default return mounted field value, but you can use getFieldsValue(true) to get all values(nameList?: NamePath[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any
isFieldTouchedCheck if a field has been operated(name: NamePath) => boolean
isFieldsTouchedCheck if fields have been operated. Check if all fields is touched when allTouched is true(nameList?: NamePath[], allTouched?: boolean) => boolean
resetFieldsReset fields to initialValues initialValues(fields?: FieldData[]) => void
setFieldsSet fields status(fields: FieldData[]) => void
setFieldValueSet the value by the field name(name: NamePath, value: any) => void
setFieldsValueSet fields value(Will directly pass to form store. If you do not want to modify passed object, please clone first)(values) => void
submitSubmit the form. It's same as click submit button() => void
validateFieldsValidate fields. Set validateOnly to true to validate without updating UI(nameList?: NamePath[], options?: ValidateOptions) => Promise & (options?: ValidateOptions) => Promise

validateMessages

Form provides default verification error messages. You can modify the template by configuring validateMessages property. A common usage is to configure localization:

const validateMessages = {
  required: "'${name}' is required!",
  // ...
};
<Form validateMessages={validateMessages} />;

CSS Variables

NameDescriptionDefault
--border-bottomBorder style of the form bottomsolid 1px var(--adm-border-color)
--border-innerBorder style between form itemssolid 1px var(--adm-border-color)
--border-topBorder style of the form topsolid 1px var(--adm-border-color)
--prefix-widthLabel width of form items in horizontal layout6.8em

Form.Item

Props

NameDescriptionTypeDefault
childElementPosition <Experimental></Experimental>Position of the widget.'normal' | 'right''normal'
dependenciesSet the dependency field. See belowNamePath[]-
disabledWhether it is disabledbooleanThe disabled of parent Form
getValueFromEventSpecify how to get value from event or other onChange arguments(..args: any[]) => any-
getValuePropsCustomize additional props with value. This prop will disable valuePropName(value) => any-
hasFeedbackWhether to show error feedbackbooleantrue
helpPrompt textReactNode-
helpIconPrompt iconReactNode<QuestionCircleOutline />
hiddenHide this fieldbooleanfalse
initialValueConfig sub default value. Form initialValues get higher priority when conflict.any-
labelLabel nameReactNode-
layoutLayout mode'vertical' | 'horizontal'The layout of parent Form
messageVariablesThe default validate field infoRecord<string, string>-
nameField name, support arrayNamePath-
normalizeNormalize value from component value before passing to Form instance. Do not support async(value, prevValue, prevValues) => any-
noStyleNo styles, only use field managementbooleanfalse
onClickThe click event of the list item.(e:React.MouseEvent, widgetRef: React.MutableRefObject<any>)-
preserveKeep field value even when field removedbooleantrue
requiredWhether it is required. Note that this property is only used to control the appearance and does not contain validation logic.booleanfalse (if rules is set, it would be judged according to rules)
rulesRules for field validation.Rule[]-
shouldUpdateCustom field update logic. See belowboolean | (prevValue, curValue) => booleanfalse
triggerWhen to collect the value of children node.stringonChange
validateFirstWhether stop validate on first rule of error for this field. Will parallel validate when parallel cofiguredboolean | 'parallel'false
validateTriggerWhen to validate the value of children nodestring | string[]onChange
valuePropNameProps of children node, for example, the prop of Switch is 'checked'. This prop is an encapsulation of getValueProps, which will be invalid after customizing getValuePropsstringvalue

The layout of Form.Item is based on List.Item. So it also supports these props of List.Item:

extra clickable arrow description

A control wrapped by Form.Item with the name property set, the form control will automatically add value (or other properties specified by valuePropName) onChange (or other properties specified by trigger), data synchronization will be taken over by Form. So if you set a name property on Form.Item, make sure its children is a valid ReactElement control and can accept the value' mentioned above and onChange properties (or other properties specified), for example:

<Form.Item name='foo'>
  <Input />
</Form.Item>

And the following spellings are wrong:

<Form.Item name='foo'>
  <Input />
  <div>hello</div>
</Form.Item>
// Wrong: Form.Item's children contain multiple elements
<Form.Item name='foo'>
  hello
  <Input />
</Form.Item>
// Wrong: Same as above, Form.Item's children contains multiple elements
<Form.Item name='foo'>
  <div>
    <Input />
  </div>
</Form.Item>
// Wrong: Form.Item's children is actually a div, and div cannot accept value and onChange properties

Also, please note:

  1. You shouldn't use onChange on each form control to collect data(use onValuesChange of Form), but you can still listen to onChange.
  2. You cannot set value for each form control via value or defaultValue prop, you should set default value with initialValues of Form. Note that initialValues cannot be updated by setState dynamically, you should use setFieldsValue in that situation.
  3. You shouldn't call setState manually, please use form.setFieldsValue to change value programmatically.

For example, the following code is wrong:

<Form.Item name='foo'>
  <Input
    value={myInputValue} // Wrong: Value should not be manually controlled
    onChange={(v) => { setMyInputValue(v) }} // Wrong: Although you can listen to the onChange event, you should not maintain your own state here
  />
</Form.Item>

dependencies

Used when there are dependencies between fields. If a field has the dependencies prop, this field will automatically trigger updates and validations when upstream is updated. A common scenario is a user registration form with "password" and "confirm password" fields. The "Confirm Password" validation depends on the "Password" field. After setting dependencies, the "Password" field update will re-trigger the validation of "Check Password".

dependencies shouldn't be used together with shouldUpdate, since it may result in conflicting update logic.

shouldUpdate

Form updates only the modified field-related components for performance optimization purposes by incremental update. In most cases, you only need to write code or do validation with the dependencies property. In some specific cases, such as when a new field option appears with a field value changed, or you just want to keep some area updating by form update, you can modify the update logic of Form.Item via the shouldUpdate.

When shouldUpdate is true, any Form update will cause the Form.Item to be re-rendered. This is very helpful for custom rendering some areas:

<Form.Item shouldUpdate>
  {() => {
    return <pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>;
  }}
</Form.Item>

When shouldUpdate is a function, it will be called by form values update. Providing original values and current value to compare. This is very helpful for rendering additional fields based on values:

<Form.Item shouldUpdate={(prevValues, curValues) => prevValues.additional !== curValues.additional}>
  {() => {
    return (
      <Form.Item name="other">
        <Input />
      </Form.Item>
    );
  }}
</Form.Item>

messageVariables

You can modify the default verification information of Form.Item through messageVariables.

<Form>
  <Form.Item messageVariables={{ another: 'good' }} label="user">
    <Input />
  </Form.Item>
  <Form.Item messageVariables={{ label: 'good' }} label={<span>user</span>}>
    <Input />
  </Form.Item>
</Form>

Custom field

Customized or third-party form controls can be used in Form, too. Controls must follow these conventions:

  • It has a controlled property value.
  • It has event onChange.

Form.Header

You can use Form.Header to group form items.

Props

NameDescriptionTypeDefault
childrenGroup header. If not passed, it will be rendered as a delimiter with no content.ReactNode-

Form.Subscribe

Props

NameDescriptionTypeDefault
childrenRender function.(changedValues: Record<string, any>, form: FormInstance) => ReactNode-
toSame as the dependencies prop of Form.Item.NamePath[]-

useWatch

Demo

Form.Array <Experimental></Experimental>

Provides array management for fields.

NameDescriptionTypeDefault
childrenRender function.(fields: FormArrayField[], operation: FormArrayOperation) => ReactElement[]-
initialValueConfig sub default value. Form initialValues get higher priority when conflict.any[]-
nameField name, support array.NamePath[]-
onAddCustom add function.(operation: FormArrayOperation) => void-
renderAddRender the content of add button.() => ReactNode-
renderHeaderRender the header of each field.(field: FormArrayField, operation: FormArrayOperation) => ReactNode-

FormArrayField

NameDescriptionType
indexThe array index.number
keyThe unique key.number

FormArrayOperation

The operation functions for Form.Array.

NameDescriptionType
addAdd a field.(initValue: any) => void
removeRemove a field.(index: number) => void

Demo

Some Common Type Definitions

NamePath

string | number | (string | number)[]

ValidateOptions

NameDescriptionTypeDefault
validateOnlyValidate only, without updating field UI state (e.g. error message display)booleanfalse
recursiveRecursive validate, validate all fields that contain the specified namePathbooleanfalse
dirtyOnly validate fields that have been modifiedbooleanfalse

FieldData

NameDescriptionType
errorsError messagesstring[]
nameField name pathNamePath[]
touchedWhether is operatedboolean
validatingWhether is in validatingboolean
valueField valueany

Rule

Rule supports a config object, or a function returning config object:

type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
NameDescriptionType
defaultFieldValidate rule for all array elements, valid when type is arrayrule
enumMatch enum value. You need to set type to enum to enable thisany[]
lenLength of string, number, arraynumber
maxtype required: max length of string, number, arraynumber
messageError message. Will auto generate by template if not providedstring
mintype required: min length of string, number, arraynumber
patternRegex patternRegExp
requiredRequired fieldboolean
transformTransform value to the rule before validation(value) => any
typeNormally string |number |boolean |url | email. More type to ref herestring
validateTriggerSet validate trigger event. Must be the sub set of validateTrigger in Form.Itemstring | string[]
validatorCustomize validation rule. Accept Promise as return. See example(rule, value) => Promise
warningOnlyWarning only. Not block form submitboolean
whitespaceFailed if only has whitespace, only work with type: 'string' ruleboolean

FAQ

How does Form.Item work with Picker / DatePicker / CascadePicker?

First, we can render the currently selected value through the children rendering function of Picker. Here we have taken DatePicker as an example, but the same is true for the other two Pickers:

<DatePicker>
  {value =>
    value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
  }
</DatePicker>

Next we need to deal with the show/hide state of the Picker, which is where the Picker component is the most different and confusing from other form components. If we put the Picker directly in the Form.Item, there is no way to show it to the user, no matter how clicked, it will not let Picker pops up:

<Form.Item
  name='birthday'
  label='Birthday'
>
  <DatePicker>
    {value =>
      value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
    }
  </DatePicker>
</Form.Item>

In most cases, the effect we need to achieve is that clicking on the outer Form.Item will trigger the display of the inner Picker. However, on Form.Item, how can I control the Picker? Maybe you'll want to declare a state yourself to control manually, for example:

const [visible, setVisible] = useState(false)
<Form.Item
  name='birthday'
  label='Birthday'
  onClick={() => {
    setVisible(true)
  }}
>
  <DatePicker
    visible={visible}
    onClose={() => {
      setVisible(false)
    }}
  >
    {value =>
      value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
    }
  </DatePicker>
</Form.Item>

But this is too cumbersome to write, and if there are multiple Pickers in a form or it is used with Form.Array, it will crash.

So antd-mobile provides a convenient method, you can directly get the ref of the internal children in the onClick event of Form.Item, so we can write:

<Form.Item
  name='birthday'
  label='Birthday'
  onClick={(e, datePickerRef: RefObject<DatePickerRef>) => {
    datePickerRef.current?.open() // ⬅️
  }}
>
  <DatePicker>
    {value =>
      value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
    }
  </DatePicker>
</Form.Item>

Finally, don't forget that the Picker component's confirmation event is onConfirm not onChange, so you need to configure trigger:

<Form.Item
  name='birthday'
  label='Birthday'
  trigger='onConfirm'  // ⬅️
  onClick={(e, datePickerRef: RefObject<DatePickerRef>) => {
    datePickerRef.current?.open()
  }}
>
  ...
</Form.Item>