Skip to content

FormField

A FormField is a reference to a slice of your form’s state. Many of Formula’s hooks accept a FormField as a parameter, and the in-built form control components all accept a FormField as a prop.

The return value of useForm is a FormField, just with a few additional capabilities compared to other FormFields.

type FormField<Data, Writable extends boolean> = {
/** Get the path of the field, joined with periods, e.g. "users.0.username" */
toString: () => string
/** Get the current data for the field */
getData: () => Readonly<Data>
/** Add a callback which will be called when the data for this field changes */
addDataListener: (listener: Listener<Data>) => Unsubscribe
/** Get the current validation errors for this field */
getErrors: () => ReadonlyArray<StandardSchemaV1.Issue>
/** Set the current validation errors for this field */
setErrors: (errors: ReadonlyArray<string | StandardSchemaV1.Issue>) => void
/** Add a callback which will be called when the errors for this field change */
addErrorListener:
(listener: Listener<ReadonlyArray<StandardSchemaV1.Issue>>) => Unsubscribe
/** Get ALL validation errors for this field, including errors for subfields */
getDeepErrors: () => ReadonlyArray<StandardSchemaV1.Issue>
/**
* Get the current blur status for this field, i.e. whether the field has lost
* focus
*/
isBlurred: () => boolean
/** Set the current blur status for this field */
setIsBlurred: (blurred: boolean) => void
/**
* Add a callback which will be called when the blur status for this field
* changes
*/
addBlurListener: (listener: Listener<boolean>) => Unsubscribe
/** Get the current changed status for this field */
isChanged: () => boolean
/** Set the current changed status for this field */
setIsChanged: (isChanged: boolean) => void
/**
* Add a callback which will be called when the change status for this field
* changes
*/
addIsChangedListener: (listener: Listener<boolean>) => Unsubscribe
/**
* Narrow the form field's type to a subtype. This is useful when your form data is
* polymorphic.
*
* @param witness (optional). This is unused except for type inference. The witness
* is likely the result of observing the field data with `useFieldData` and
* narrowing its type based on some condition
*/
narrow: <SubType extends Data>(witness?: SubType) => FormField<SubType>
} & (Writable extends true ? {
/** Set the data for the field */
setData: (setter: Setter<Data>, opts?: SetDataOpts) => void
} : {});
/** A value or replacement function used for updating a value */
type Setter<T> = T | ((prevData: T) => T)
type SetDataOpts = {
/**
* Whether to validate the data after setting it. If not specified,
* will validate if `useForm#validateOnChange` is true.
*/
shouldValidate?: boolean;
/**
* The change status for the field after setting the data.
* 'retain' will leave the change status untouched
* @default true
*/
nextChangeStatus?: boolean | "retain";
}

If Data is an object, the type of FormField is additionally a function which can be called with keys of the object to obtain a FormField for the subfield.

type GetObjectKey<Data extends object> =
<K extends keyof Data>(key: K) => FormField<Data[K]>

If Data is an array, the type of FormField is additionally a function which can be called with an array index to obtain a FormField for that element.

There are also convenience methods for common operations.

type GetArrayIndex<E> = (idx: number) => FormField<E | undefined, E>;
type ArrayMethods<E> = {
/** Push one or more elements onto the end of the array */
push: (...items: E[]) => void
/** Remove the element at the specified index */
remove: (index: number) => void
}