Demos
Required and Optional Fields
To make all fields required, set the required property on the Form.Handler component.
For fields that should remain optional, use required={false} property on the specific field. When doing so, it will append "(optional)" to the optional field's label(labelSuffix).
<Form.Handler required> <Form.Card> <Field.Email path="/email" required={false} /> <Field.String path="/custom" label="Label" labelDescription="Label description" required={false} /> <Field.Currency path="/amount" label="Amount" /> <Form.SubmitButton /> </Form.Card> </Form.Handler>
In combination with a SubmitButton
This example uses an async onSubmit event handler. It will disable all fields and show an indicator on the Form.SubmitButton while the form is pending.
With an async function, you can also handle the response from the server and update the form with the new data.
// Async functionconst onSubmit = async (data) => {try {const response = await fetch('https://api.example.com', {method: 'POST',body: JSON.stringify(data),})const data = await response.json()Form.setData('unique', data) // Whatever you want to do with the data} catch (error) {return error // Will display the error message in the form}}
<Form.Handler onSubmit={async (data) => console.log('onSubmit', data)}> <Form.Card> <Field.Email path="/email" /> <Form.ButtonRow> <Form.SubmitButton /> </Form.ButtonRow> </Form.Card> </Form.Handler>
New location after async submit
This example is only for demo purpose and will NOT redirect to a new location. It will also time out after 10 seconds.
<Form.Handler data={{ myField: 'Some value', }} onSubmit={async (data) => { console.log('onSubmit', data) // Wait for 2 seconds await new Promise((resolve) => setTimeout(resolve, 2000)) // e.g. go to new location // Optionally, you can return e.g. the "pending" status with an additional info return { info: 'Redirecting to a new location', // Force the form to stay in pending state status: 'pending', } }} asyncSubmitTimeout={10000} > <Flex.Stack> <Form.MainHeading>Heading</Form.MainHeading> <Form.Card> <Value.String label="Summary" path="/myField" /> </Form.Card> <Form.ButtonRow> <Form.SubmitButton /> </Form.ButtonRow> </Flex.Stack> </Form.Handler>
Reduce your data to visible fields
You can use the reduceToVisibleFields function to get only the data of visible (mounted) fields.
<Form.Handler defaultData={{ isVisible: true, }} onSubmit={(data, { reduceToVisibleFields }) => { const myData = reduceToVisibleFields(data, { removePaths: ['/isVisible'], }) console.log('Result of reduceToVisibleFields: ', myData) }} > <Flex.Stack> <Field.Boolean label="Show radio buttons" variant="button" path="/isVisible" /> <Form.Visibility pathTrue="/isVisible" animate> <Field.Selection label="Radio buttons" variant="radio" path="/myValue" defaultValue="foo" > <Field.Option value="foo" title="Foo" /> <Field.Option value="bar" title="Bar" /> </Field.Selection> </Form.Visibility> </Flex.Stack> </Form.Handler>
With session storage
Changes you make to the fields are temporarily saved and loaded when the browser reloads. The data is stored until the session storage is invalidated.
<Form.Handler onSubmit={(data, { resetForm, clearData }) => { console.log('onSubmit', data) // Docs: https://eufemia.dnb.no/uilib/extensions/forms/DataContext/Provider/events/#onsubmit-parameters resetForm() clearData() }} sessionStorageId="session-key" > <Form.Card> <Field.String label="Name" path="/name" /> <Field.Email path="/email" /> <Form.ButtonRow> <Form.SubmitButton /> </Form.ButtonRow> </Form.Card> </Form.Handler>
Locale and translations
const myTranslations = { 'nb-NO': { PhoneNumber: { label: 'Egendefinert 🚀', }, }, 'en-GB': { PhoneNumber: { label: 'Custom 🚀', }, }, } const MyForm = () => { const { data } = Form.useData('my-form', { locale: 'en-GB', }) return ( <Form.Handler id="my-form" locale={data?.locale} translations={myTranslations} > <Form.Card> <Field.PhoneNumber /> <Field.Selection path="/locale" variant="button" optionsLayout="horizontal" > <Field.Option value="nb-NO">Norsk</Field.Option> <Field.Option value="sv-SE">Svenska</Field.Option> <Field.Option value="da-DK">Dansk</Field.Option> <Field.Option value="en-GB">English</Field.Option> </Field.Selection> </Form.Card> </Form.Handler> ) } render(<MyForm />)
Autocomplete (autofill) user data
<Form.Handler onSubmit={(data) => console.log('onSubmit', data)} autoComplete > <Flex.Stack> <Form.MainHeading>Delivery address</Form.MainHeading> <Form.Card> <Form.SubHeading>Your name</Form.SubHeading> <Field.Name.First path="/firstName" required /> <Field.Name.Last path="/lastName" required /> </Form.Card> <Form.Card> <Form.SubHeading>Your address</Form.SubHeading> <Field.Composition width="large"> <Field.String label="Street" width="stretch" path="/streetName" required /> <Field.Number label="Nr." width="small" path="/streetNr" required /> </Field.Composition> <Field.PostalCodeAndCity postalCode={{ required: true, path: '/postalCode', }} city={{ required: true, path: '/city', }} /> </Form.Card> <Form.Card> <P>More information about this form.</P> <Form.ButtonRow> <Form.SubmitButton /> </Form.ButtonRow> </Form.Card> </Flex.Stack> </Form.Handler>
Complex async (autosave) example
This example demonstrates how to use async validation with an async onSubmit and async onChange event for both the Form.Handler and a field itself.
-
While you write, an async validation request is simulated to check if the input is valid. If it's not, an error message will be shown.
-
During validation, only the relevant value will be evaluated. This means, when the delayed validation is done, and the value has changed, the validation result will be omitted.
-
You can press enter to submit the form while you write. But only a string of
validwill be accepted to emit the formonSubmitandonChange. -
You can start writing, wait a second or two and remove the whole text again and blur the field. The async validation return will be omitted and the "required" error message will be shown.
-
It also shows some status messages after the validation and submit requests are done.
-
This example does not include an async
onBlurValidator– but it's possible to add one into the mix as well. -
To access the
date"in sync" – you can use the Form.useData hook.
const validator = debounceAsync(async function secondValidator( value: string, ) { try { const request = createRequest() const wasCanceled = this.addCancelEvent(request.cancel) await request(2000) // Simulate a request if (wasCanceled()) { throw new Error('Validation request canceled') } } catch (error) { return error } if (value !== 'valid') { return new Error(`Custom error with invalid value: ${value}`) // Show this message } }) const cancelRequest = () => { validator.cancel() } const onSubmit = async (data) => { console.log('onSubmit', data) // Wait for 2 seconds await new Promise((resolve) => setTimeout(resolve, 2000)) // For demo purposes, we show a message return { info: 'Message from onSubmit return', } } const onChangeForm = async (data) => { console.log('onChangeForm', data) // Wait for 2 seconds await new Promise((resolve) => setTimeout(resolve, 2000)) // For demo purposes, we show a message return { warning: 'Warning message', } } const onChangeField = async (data) => { console.log('onChangeField', data) // Wait for 2 seconds await new Promise((resolve) => setTimeout(resolve, 2000)) // For demo purposes, we show a message return { info: 'Info message', } } const MyForm = () => { const { data } = Form.useData('unique-id') console.log('data', data) return ( <Form.Handler id="unique-id" onSubmit={onSubmit} onChange={onChangeForm} > <Flex.Stack> <Field.String label='Type "valid" to validate the field' path="/myField" required onChangeValidator={validator} onChange={onChangeField} autoComplete="off" /> <Form.ButtonRow> <Form.SubmitButton text="Save" /> <Button text="Stop async operations" variant="tertiary" icon={stopIcon} icon_position="left" disabled={false} onClick={cancelRequest} /> </Form.ButtonRow> </Flex.Stack> </Form.Handler> ) } render(<MyForm />)
Filter your data
By using the filterData method from the onSubmit event callback you can filter out data that you don't want to send to your server.
More info about filterData can be found in the Getting Started section.
In this example we filter out all fields that are disabled.
const id = 'my-form' const filterDataHandler = ({ props }) => !props.disabled const MyForm = () => { const { data } = Form.useData(id, { disabled: false, myField: 'Value', }) return ( <Form.Handler id={id} onSubmit={(data, { filterData }) => { console.log('onSubmit', filterData(filterDataHandler)) }} > <Flex.Stack> <Field.Boolean label="Disabled" path="/disabled" /> <Field.String label="My Field" path="/myField" disabled={data.disabled} /> <Form.ButtonRow> <Form.SubmitButton /> </Form.ButtonRow> </Flex.Stack> </Form.Handler> ) } const Output = () => { const { filterData } = Form.useData(id) const { hasErrors } = Form.useValidation(id) return ( <> <Tools.Log top data={hasErrors()} label="hasErrors:" /> <Tools.Log top data={filterData(filterDataHandler)} /> </> ) } render( <> <MyForm /> <Output /> </>, )
Transform data
You can use the transformData method from the onSubmit event callback to transform the data before sending it to your server.
It's possible to use the transformOut on the Form.Handler method to achieve the same. But performance wise, it's better to use the transformData method. This is because transformOut on the Form.Handler method will execute for every change, while transformData method from the onSubmit event callback only executes when submitting the form.
const MyForm = () => { const [submitData, setSubmitData] = React.useState({}) const onSubmit = (data, { transformData }) => { const transformedData = transformData( data, ({ value, displayValue, label }) => { return { value, displayValue, label, } }, ) setSubmitData(transformedData) console.log('onSubmit', transformedData) } return ( <Form.Handler onSubmit={onSubmit}> <Flex.Stack> <Field.String label="Foo label" path="/myString" defaultValue="foo" /> <Field.Selection label="Bar label" path="/mySelection" defaultValue="bar" variant="dropdown" > <Field.Option value="foo" title="Foo Value" /> <Field.Option value="bar" title="Bar Value" /> </Field.Selection> <Field.ArraySelection label="Bar label" path="/myArraySelection" defaultValue={['bar']} variant="checkbox" > <Field.Option value="foo" title="Foo Value" /> <Field.Option value="bar" title="Bar Value" /> </Field.ArraySelection> <Form.SubmitButton /> <Tools.Log label="Submit Data (press submit to update)" data={submitData} /> <Tools.Log label="Data Context" /> </Flex.Stack> </Form.Handler> ) } render(<MyForm />)