VA Forms Library - How to create custom fields and widgets
How does the Forms Library use RJSF?
The Forms Library code uses react-jsonschema-form, or RJSF, to render form fields, but it builds a scaffolding on top of it to support multi-page forms and common form patterns. Additionally, the Forms Library uses RJSF to create a form configuration spec that allows developers to specify the structure of a multi-page form.
Customizing fields, widgets, and events from RJSF
RJSF passes all field and widget components to SchemaField
(and most other components) as a registry
property. To override fields and widgets in the registry, pass components of the same name into the library's main Form
component. The Forms Library uses custom versions of these components:
FieldTemplate
The Forms Library uses these custom fields and widgets:
Let's understand the implementation of a custom widget with this PhoneNumberWidget example from the PhoneNumberWidget.jsx file:
<TextWidget
{...this.props}
type="tel"
autocomplete="tel"
value={this.state.val}
onChange={this.handleChange}
/>
PhoneNumberWidget
is a custom widget which allows only numbers in an input field. We’re using the TextWidget
component and customizing the widget by adding an onChange
event which triggers the handleChange
method to restrict non-numeric values.
Now, how should we use this custom widget in a form? Let’s look at the following example in the uiSchema:
phone: {
'ui:title': 'Phone',
'ui:widget': PhoneNumberWidget,
'ui:errorMessages': {
required: 'Please enter your phone number',
pattern: 'Please enter a valid phone number',
},
}
To use the custom component we created, we define uiSchema and pass the component PhoneNumberWidget
to 'ui:widget'
.
You can learn more about custom widgets and fields in the RJSF documentation. NOTE: The Forms Library doesn’t use the latest version of the RJSF library, so some of the features or options in this documentation may be unavailable when working with the Forms Library.
Custom widget interface
Writing custom widgets is similar to writing React components: A value is passed in, and an onChange
hook is provided for changing data. Other properties like the schemas and field ID are also provided.
These are the properties passed to all custom widget components:
Properties | Description |
---|---|
disabled | Boolean to indicate if the field has been disabled through the schema. |
formContext | The form context object from RJSF. |
id | The string id for the particular field being used. |
label | The label text for the field, typically the title from the schema. |
onChange | Function that will save data entered in the widget into your form data. |
onBlur | Function that marks the field as "blurred," which will reveal any validation errors for that field. |
options | The |
options.enumOptions | An array of enum options and their names, if present in the schema, inserted into |
readonly | If the field is marked |
registry | The registry of field and widgets, generally used by fields to choose the right component to use. |
required | If the field is required or not via the schema. |
value | The form data for the current property. |
Custom Field interface
Widgets are typically meant for input type components where data is saved to a single property in your form data. The properties they receive are derived the broader properties of field components.
The main differences are the inclusion of idSchema
and uiSchema
, which along with schema
allow full access to configuration info for a particular property in your form config.
These are the properties passed to field components:
Properties | Description |
---|---|
disabled | Boolean to indicate if the field has been disabled through the schema. |
formContext | The form context object from RJSF. |
formData | The form data for the current property. |
idSchema | An object with IDs for the current property and any sub properties. The |
name | The name of the current property, from |
onChange | Function that will save data entered in the widget into your form data. |
onBlur | Function that marks the field as "blurred," which will reveal any validation errors for that field. |
readonly | If the field is marked |
registry | The registry of field and widgets, generally used by fields to choose the right component to use. |
required | If the field is required or not via the schema. |
uiSchema | The |
Events
In addition to customizing fields and widgets, the Forms Library code hooks into a number of events provided by Form
to support the form patterns found in the FormPage
component. These events are:
validate
: This event is called when validation occurs. We call our custom validation, which reads uiSchema for custom validation hooks that have been included for form fields outside of what JSON Schema provides.transformErrors
: This event is provided when the Forms Library receives the list of JSON Schema validation errors and can return a transformed list. It replaces the messages with a set of default messages, as well as any messages provided for specific fields in uiSchema. It also moves the errors for required fields from the object level to the field level. Because JSON Schema specifies required fields with arequired
array on an object field schema, any errors about missing data are associated with that object and moved so they're associated with the missing field and rendered with that field on the form.onError
: This event is called if a user tries to submit a form with a validation error. The Forms Library sets asubmitted
flag informContext
, which is an object passed to all fields and components in the RJSF form. TheFieldTemplate
component usesformContext
to display all error messages to the user.onSubmit
: This event is called when a user submits a form with no validation errors. When this happens, the Forms Library code looks for the next page in the multi-page form and navigates to it.onChange
: This event is called when a user changes data in the form. The Forms Library fires a Redux action and updates the store with the new data. The reducer code does several recalculations:Required fields for the schema: You can specify functions in uiSchema that set fields as optional or required based on form data. This runs them and updates the schema.
Remove data from hidden fields: In uiSchema, you can specify fields that are conditionally hidden based on user data. To avoid validation errors from data a user can't see, the Forms Library updates the schema to add a
ui:hidden
property and remove any user data for those fields.General schema updates: Because you can make arbitrary changes to the schema based on form data, the Forms Library must also make those changes; for example, removing options in an
enum
array when a user has entered certain data.
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.