Create a form
Forms can be freely composed by using the available form field components and optionally wrapping them in Form
A simple code example is given below.
Basic AP I of any form field component
When you look at the API of any form field component you'll notice they all share the same or very similar props. The most important props for using a form field component are:
-
value(s)
: should be set to either null or an appropriate value for the specific form field component. Check the API docs of your specific form field component for more details. -
on
: a callback that is called whenever the internal state of the form field changes as a result of some sort of user input. This is always called with the current internal value(s).Change -
validate
: a callback that is called when the form field is first mounted and whenever the value of the component changes after setting/updating thevalue(s)
prop. This is always called with the current internal value(s).
The basic React pattern to implement a form field then becomes:
-
Store the value of the
value(s)
prop in the state of a parent component that renders the form field component and pass it to the form field component. -
Handle the
on
callback by updating the value(s) in the state to the given value(s) using setState.Change -
Optionally implement a
validate
callback to return null if all is well or an object with aconnotation
property set to either ‘warning’, ‘error’ or ‘info’ and an optionalmessage
property set to any string.
Using form field components standalone
The simplest implementation of a form is a single form field without any other components to support it. An example could be a Popover that shows a single autocomplete field to select a value for an attribute of an element. This could simply be implemented by using a Singleon
callback and setting the value
prop as described in the basic pattern to implement a form field above.
An example of a popover that follows this basic pattern would be:
JavaScript
import React, { useState } from 'react';
import { Popover, PopoverBody, PopoverHeader, SingleAutocomplete } from 'fds/components';
const items = [/* omitted for brevity */];
function ExamplePopover() {
const [value, setValue] = useState(null);
return (
<Popover>
<PopoverHeader title="Edit some field name" />
<PopoverBody>
<SingleAutocomplete
items={items}
onChange={setValue}
value={value}
/>
</PopoverBody>
</Popover>
);
}
export default ExamplePopover;
Note: validate is not implemented in this case. You could implement it if the value has to be set, but then you would have to make sure there is an initial/fallback value at all times because a single autocomplete can be cleared by the user. That would only complicate this code example.
Showing labels next to form field components
In order to neatly show labels before or above each form field component, a Formlabel
prop as desired and its label
prop to either ‘before’ or ‘above’. When applied to the earlier example, the render function could return something like this instead:
JavaScript
<Popover>
<PopoverBody>
<FormRow label="Edit some field name" labelPosition="above">
<SingleAutocomplete
items={items}
onChange={setValue}
value={value}
/>
</FormRow>
</PopoverBody>
</Popover>
Combining multiple form components in a single Form Row
It is also possible to wrap a single form row around multiple form field components. In that case, you can use the FormRow’s space
prop. If you like to layout multiple form fields next to each other instead of below each other, simply use a Flex component or any of the other FDS layout containers around those form fields and wrap them in a form row to render the label.
If you don’t want any labels before or above your form field(s), there is no need to use the FormRow component at all.
Using a Form component to aggregate the feedback and values of all fields at once
The main reason to use a Form component is to easily manage multiple related form fields as a single unit of data. This is usually best when the form should be submitted only when it’s complete and has an explicit submit button to do so. This could be the case when the form is submitted to an external API.
If your form field value(s) should be saved to the XML directly, we recommend handling each form field separately.
If you do so, no submit button is necessary and each field can automatically ‘save’ its value directly to the XML. Ideally by executing an appropriate operation whenever the onChange callback of the form field changes.
This approach is stubbed in the ‘No form’ example on the form example on the FDS playground. Along with some other examples of using Form, Form
There are helper functions to aggregate and display all feedback and values. These are also demonstrated on the form example on the playground. Also the API docs for Form, Form
Extra considerations when writing form field validation functions
Performance
Validate functions are called often (during each re-render of every field in the form) so make sure they are performant and as cheap to execute as possible. In a future release we will memoize these functions, so repeated calls with the same input value will just use the memoized value instead of running the actual validate function again.
Make sure the validate function is a pure function, meaning it is only dependent on its input value and does not use or modify any external data/state. This is necessary because you cannot manually trigger form validation and updating any resulting feedback based on external data/state changes.
Related / interdependent fields
The previous note also illustrates why it is currently not (cleanly/easily) possible to make form fields depend on each other. This will be addressed in a future release.
If you need to make a form where fields depend on each other, consider contacting Fonto with your requirements as early as possible so we can take those into consideration when designing the API and UX to do so supported by the SDK.
If you cannot wait for that, you can build it yourself, but you would need to build some of the form functionality yourself as well.
This includes manually tracking and rendering the value and feedback for each field and calling a custom validation routine when handling the on
callback. That might not be very complex but does require a lot of manual imperative ‘bookkeeping’ code. Think lots of callback handling and setState calls, which we would eventually like to implement within the form (field) components and expose through a declarative API, such as props on those components.
To render the feedback yourself, you can manually compose it by using , and components around and next to your form field component.