Forms
XMLUI enables to create forms without the hassle of managing, editing, validating, and saving the information you provide in the UI.
<App>
<Form data="{{ name: 'Joe', age: 43 }}">
<FlowLayout>
<H3>Customer information</H3>
<FormItem bindTo="name" label="Customer name" />
<FormItem bindTo="age" label="Age" type="integer" zeroOrPositive="true" />
</FlowLayout>
</Form>
</App>
<App>
<Form data="{{ name: 'Joe', age: 43 }}">
<FlowLayout>
<H3>Customer information</H3>
<FormItem bindTo="name" label="Customer name" />
<FormItem bindTo="age" label="Age" type="integer" zeroOrPositive="true" />
</FlowLayout>
</Form>
</App>
Form
encapsulates the management of form UI elements and data handling- The
data
property holds the form's data FormItem
manages a piece of the databindTo
specifies the property name within thedata
to bind the corresponding fieldtype
determines the kind of input field needed for a given piece of data (number field, text area field, radio buttons, etc.)- other properties support styling or validation
Form Layouts
You can use any of XMLUI's layout mechanisms with a
Form
. Here is a single-column format using FlowLayout
.<App>
<Form data="{
{
firstname: 'Jake',
lastname: 'Hard',
jobTitle: 'janitor',
experience: 'broom'
}
}">
<FlowLayout>
<FormItem label="Firstname" bindTo="firstname" />
<FormItem label="Lastname" bindTo="lastname" />
<FormItem label="Job Title" bindTo="jobTitle" />
<FormItem label="Experience" bindTo="experience" />
</FlowLayout>
</Form>
</App>
<App>
<Form data="{
{
firstname: 'Jake',
lastname: 'Hard',
jobTitle: 'janitor',
experience: 'broom'
}
}">
<FlowLayout>
<FormItem label="Firstname" bindTo="firstname" />
<FormItem label="Lastname" bindTo="lastname" />
<FormItem label="Job Title" bindTo="jobTitle" />
<FormItem label="Experience" bindTo="experience" />
</FlowLayout>
</Form>
</App>
Set each item's width to
50%
to create a two-column layout.<App>
<Form
data="{{
firstname: 'Jake',
lastname: 'Hard',
jobTitle: 'janitor',
experience: 'broom'
}}">
<FlowLayout>
<FormItem label="Firstname" bindTo="firstname" width="50%" />
<FormItem label="Lastname" bindTo="lastname" width="50%" />
<FormItem label="Job Title" bindTo="jobTitle" width="50%" />
<FormItem label="Experience" bindTo="experience" width="50%" />
</FlowLayout>
</Form>
</App>
<App>
<Form
data="{{
firstname: 'Jake',
lastname: 'Hard',
jobTitle: 'janitor',
experience: 'broom'
}}">
<FlowLayout>
<FormItem label="Firstname" bindTo="firstname" width="50%" />
<FormItem label="Lastname" bindTo="lastname" width="50%" />
<FormItem label="Job Title" bindTo="jobTitle" width="50%" />
<FormItem label="Experience" bindTo="experience" width="50%" />
</FlowLayout>
</Form>
</App>
Use star sizing to allocate widths flexibly. Here
Firstname
and Lastname
equally share the space remaining after the 100-px-wide Title
.<App>
<Form
data="{{
title: 'Mr.',
firstname: 'Jake',
lastname: 'Hard',
jobTitle: 'janitor',
experience: 'broom'
}}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FlowLayout>
<HStack>
<FormItem label="Title" bindTo="title" width="100px" />
<FormItem label="Firstname" bindTo="firstname" width="*" />
<FormItem label="Lastname" bindTo="lastname" width="*" />
</HStack>
<FormItem label="Job Title" bindTo="jobTitle" width="50%" />
<FormItem label="Experience" bindTo="experience" width="50%" />
</FlowLayout>
</Form>
</App>
<App>
<Form
data="{{
title: 'Mr.',
firstname: 'Jake',
lastname: 'Hard',
jobTitle: 'janitor',
experience: 'broom'
}}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FlowLayout>
<HStack>
<FormItem label="Title" bindTo="title" width="100px" />
<FormItem label="Firstname" bindTo="firstname" width="*" />
<FormItem label="Lastname" bindTo="lastname" width="*" />
</HStack>
<FormItem label="Job Title" bindTo="jobTitle" width="50%" />
<FormItem label="Experience" bindTo="experience" width="50%" />
</FlowLayout>
</Form>
</App>
FormItem
FormItem
is an intermediary layer between Form
and an input component; it manages the data represented by that component. The type
property of a FormItem
specifies what input component to render.Checkbox
<App>
<Form data="{{ option1: true, option2: false, option3: true }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="checkbox" bindTo="option1" label="Option #1" labelPosition="end" />
<FormItem type="checkbox" bindTo="option2" label="Option #2" labelPosition="end" />
<FormItem type="checkbox" bindTo="option3" label="Option #3" labelPosition="end" />
</Form>
</App>
<App>
<Form data="{{ option1: true, option2: false, option3: true }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="checkbox" bindTo="option1" label="Option #1" labelPosition="end" />
<FormItem type="checkbox" bindTo="option2" label="Option #2" labelPosition="end" />
<FormItem type="checkbox" bindTo="option3" label="Option #3" labelPosition="end" />
</Form>
</App>
DatePicker
<App>
<Form
data="{{ birthDate: '2021-04-08' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="datePicker" bindTo="birthDate" label="Birthdate" />
</Form>
</App>
<App>
<Form
data="{{ birthDate: '2021-04-08' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="datePicker" bindTo="birthDate" label="Birthdate" />
</Form>
</App>
File
Use
file
to select one or multiple files.<App>
<Form
data="{{ articles: null }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="file" bindTo="articles" label="Articles file" />
</Form>
</App>
<App>
<Form
data="{{ articles: null }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="file" bindTo="articles" label="Articles file" />
</Form>
</App>
Integer
<App>
<Form
data="{{ age: 30 }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="integer" bindTo="age" label="Age" />
</Form>
</App>
<App>
<Form
data="{{ age: 30 }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="integer" bindTo="age" label="Age" />
</Form>
</App>
Number
<App>
<Form
data="{{ distance: 192.5 }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="number" bindTo="distance" label="Distance in miles" />
</Form>
</App>
<App>
<Form
data="{{ distance: 192.5 }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="number" bindTo="distance" label="Distance in miles" />
</Form>
</App>
RadioGroup
<App>
<Form
data="{{ title: 'Mr.' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="radioGroup" bindTo="title" label="Title">
<Option label="Mr." value="Mr." />
<Option label="Mrs." value="Mrs." />
<Option label="Ms." value="Ms." />
<Option label="Dr." value="Dr." />
</FormItem>
</Form>
</App>
<App>
<Form
data="{{ title: 'Mr.' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="radioGroup" bindTo="title" label="Title">
<Option label="Mr." value="Mr." />
<Option label="Mrs." value="Mrs." />
<Option label="Ms." value="Ms." />
<Option label="Dr." value="Dr." />
</FormItem>
</Form>
</App>
Select
<App>
<Form
data="{{ size: 'xs' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="select" bindTo="size" label="Box size">
<Option label="Extra small" value="xs" />
<Option label="Small" value="sm" />
<Option label="Medium" value="md" />
<Option label="Large" value="lg" />
</FormItem>
</Form>
</App>
<App>
<Form
data="{{ size: 'xs' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="select" bindTo="size" label="Box size">
<Option label="Extra small" value="xs" />
<Option label="Small" value="sm" />
<Option label="Medium" value="md" />
<Option label="Large" value="lg" />
</FormItem>
</Form>
</App>
Switch
<App>
<Form
data="{{ showBorder: true, showText: false, hideShadow: true }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="switch" bindTo="showBorder" label="Show border" labelPosition="right" />
<FormItem type="switch" bindTo="showText" label="Show text" labelPosition="right" />
<FormItem type="switch" bindTo="hideShadow" label="Hide shadow" labelPosition="right" />
</Form>
</App>
<App>
<Form
data="{{ showBorder: true, showText: false, hideShadow: true }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="switch" bindTo="showBorder" label="Show border" labelPosition="right" />
<FormItem type="switch" bindTo="showText" label="Show text" labelPosition="right" />
<FormItem type="switch" bindTo="hideShadow" label="Hide shadow" labelPosition="right" />
</Form>
</App>
TextBox
<App>
<Form
data="{{ name: 'Joe' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="text" bindTo="name" label="Name" />
</Form>
</App>
<App>
<Form
data="{{ name: 'Joe' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="text" bindTo="name" label="Name" />
</Form>
</App>
TextArea
<App>
<Form
data="{{ description: 'This is a description' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="textarea" bindTo="description" label="Description" />
</Form>
</App>
<App>
<Form
data="{{ description: 'This is a description' }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem type="textarea" bindTo="description" label="Description" />
</Form>
</App>
Custom
You can create a custom input component that uses the XMLUI forms infrastructure.
<App>
<Form
data="{{ userAvailable: false }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem bindTo="userAvailable">
<HStack>
<Button
label="Toggle"
backgroundColor="{$value === false ? 'red' : 'green'}"
onClick="$setValue(!$value)"
/>
</HStack>
</FormItem>
</Form>
</App>
<App>
<Form
data="{{ userAvailable: false }}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem bindTo="userAvailable">
<HStack>
<Button
label="Toggle"
backgroundColor="{$value === false ? 'red' : 'green'}"
onClick="$setValue(!$value)"
/>
</HStack>
</FormItem>
</Form>
</App>
$value
represents the current value of the component. $setValue
changes the value.Provide data
You can define a
Form
s data structure and initial values directly.<Form data="{{ name: 'Joe', age: 43 }}" />
Or via an API endpoint.
<Form data="/path/to/resource" />
Use the
bindTo
property to access fields in the structure.<Form data="{{ name: 'Joe' }}">
<FormItem bindTo="name" />
</Form>
Refer to data
The
$data
variable holds all the form's data. You can use values in $data
to control FormItem
properties. Here the Switch
s value sets the enabled
property of a FormItem
.<App>
<Form data="{{ isEnabled: true, name: 'Joe' }}">
<FormItem label="Enable name" bindTo="isEnabled" type="switch" />
<FormItem enabled="{$data.isEnabled}" label="Name" bindTo="name" />
</Form>
</App>
<App>
<Form data="{{ isEnabled: true, name: 'Joe' }}">
<FormItem label="Enable name" bindTo="isEnabled" type="switch" />
<FormItem enabled="{$data.isEnabled}" label="Name" bindTo="name" />
</Form>
</App>
Other components in the form can reference the form's data too. Here the
Text
updates reactively when input values change.<App>
<Form data="{{ firstname: 'John', lastname: 'Doe' }}">
<FormItem label="Firstname" bindTo="firstname" />
<FormItem label="Lastname" bindTo="lastname" />
<Text>Full name: {$data.firstname} {$data.lastname}</Text>
</Form>
</App>
<App>
<Form data="{{ firstname: 'John', lastname: 'Doe' }}">
<FormItem label="Firstname" bindTo="firstname" />
<FormItem label="Lastname" bindTo="lastname" />
<Text>Full name: {$data.firstname} {$data.lastname}</Text>
</Form>
</App>
You can drill into
$data
to reference nested fields.<App>
<Form
data="{{
name: 'John smith',
address: { street: '96th Ave N', city: 'Seattle', zip: '98005' }
}}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem bindTo="name" label="Name" />
<FormItem bindTo="address.street" label="Street" />
</Form>
</App>
<App>
<Form
data="{{
name: 'John smith',
address: { street: '96th Ave N', city: 'Seattle', zip: '98005' }
}}"
onSubmit="(toSave) => toast.success(JSON.stringify(toSave))">
<FormItem bindTo="name" label="Name" />
<FormItem bindTo="address.street" label="Street" />
</Form>
</App>
Validate data
The
Form
handles client-side validation, reporting issues interactively. Server-side validation happens when the form data is sent to the server. The Form
handles the server's response and displays it in a summary or below input fields.These are the
FormItem
validation properties.minLength
<App>
<Form data="{{ name: 'Billy Bob' }}">
<FormItem bindTo="name" minLength="10" label="minLength" />
</Form>
</App>
<App>
<Form data="{{ name: 'Billy Bob' }}">
<FormItem bindTo="name" minLength="10" label="minLength" />
</Form>
</App>
Try submitting with fewer than 10 characters.
maxLength
<App>
<Form data="{{ name: 'Billy Bob' }}">
<FormItem bindTo="name" maxLength="11" label="maxLength" />
</Form>
</App>
<App>
<Form data="{{ name: 'Billy Bob' }}">
<FormItem bindTo="name" maxLength="11" label="maxLength" />
</Form>
</App>
Try entering more than 11 characters.
minValue
<App>
<Form data="{{ age: 30 }}">
<FormItem bindTo="age" type="number" minValue="32" label="minValue" />
</Form>
</App>
<App>
<Form data="{{ age: 30 }}">
<FormItem bindTo="age" type="number" minValue="32" label="minValue" />
</Form>
</App>
Try entering a number smaller than 32.
maxValue
<App>
<Form data="{{ age: 30 }}" >
<FormItem bindTo="age" type="number" maxValue="29" label="maxValue" />
</Form>
</App>
<App>
<Form data="{{ age: 30 }}" >
<FormItem bindTo="age" type="number" maxValue="29" label="maxValue" />
</Form>
</App>
Try entering a number larger than 32.
pattern
Evaluate predefined regex patterns: "email", "url", or "phone".
See the pattern property of
FormItem
.regex
Evaluate a custom regex pattern.
<App>
<Form data="{{ password: 'hello' }}">
<!-- Only all uppercase letters are accepted -->
<FormItem bindTo="password" regex="/^[A-Z]+$/" label="regex" />
</Form>
</App>
<App>
<Form data="{{ password: 'hello' }}">
<!-- Only all uppercase letters are accepted -->
<FormItem bindTo="password" regex="/^[A-Z]+$/" label="regex" />
</Form>
</App>
Compound validation
You can use multiple validations.
<App>
<Form data="{{ site: 'http://www.example.com' }}">
<FormItem bindTo="site" minLength="10" maxLength="30"
pattern="url" label="Multiple Validations" />
</Form>
</App>
<App>
<Form data="{{ site: 'http://www.example.com' }}">
<FormItem bindTo="site" minLength="10" maxLength="30"
pattern="url" label="Multiple Validations" />
</Form>
</App>
Validation-specific severity
By default, all validations have a severity level of "error". You can set whether a validation should have a level of "warning" or "error".
<App>
<Form data="{{ mobile: '+13456123456', website: 'http://www.blogsite.com' }}" >
<FormItem
bindTo="mobile"
pattern="phone"
patternInvalidSeverity="warning"
label="mobilePattern" />
<FormItem
bindTo="website"
pattern="url"
patternInvalidSeverity="error"
label="websitePattern" />
</Form>
</App>
<App>
<Form data="{{ mobile: '+13456123456', website: 'http://www.blogsite.com' }}" >
<FormItem
bindTo="mobile"
pattern="phone"
patternInvalidSeverity="warning"
label="mobilePattern" />
<FormItem
bindTo="website"
pattern="url"
patternInvalidSeverity="error"
label="websitePattern" />
</Form>
</App>
Validation-specific messages
Predefined validations have built-in messages that you can change.
<App>
<Form data="{{ age: 20 }}" >
<FormItem
bindTo="age"
type="number"
minValue="21"
rangeInvalidMessage="The given age is too low!"
label="Invalid Message" />
</Form>
</App>
<App>
<Form data="{{ age: 20 }}" >
<FormItem
bindTo="age"
type="number"
minValue="21"
rangeInvalidMessage="The given age is too low!"
label="Invalid Message" />
</Form>
</App>
Server-side validation
The
Form
component can receive and display a server-side validation response. Field related issues are shown just like client-side validation errors, removed when a field is edited. Non-field related issues are displayed in a validation summary view.
Submit data
By default the
Form
component provides a submit button to save the modified data.<App>
<Form onSubmit="toast('Saved!')" />
</App>
<App>
<Form onSubmit="toast('Saved!')" />
</App>
The
onSubmit
accepts either a block of code or function. When you use a function it receives data in a parameter; in this example it's called toSave
but you can use any name. The function can be defined inline, in a code-behind file, or in index.html
attached to the global window
variable. See the Code chapter for details.<App>
<Form
data="{{ name: 'Joe', age: 43 }}"
onSubmit="(d) => toast(JSON.stringify(d))"
>
<FormItem label="name" bindTo="name" />
<FormItem label="age" bindTo="age" />
</Form>
</App>
<App>
<Form
data="{{ name: 'Joe', age: 43 }}"
onSubmit="(d) => toast(JSON.stringify(d))"
>
<FormItem label="name" bindTo="name" />
<FormItem label="age" bindTo="age" />
</Form>
</App>
To submit via an
APICall
, use the event
helper tag to bridge between the form and the API. The Form
s data
attribute maps to the APICall
's $param
context variable. A Toast
popup reports success or error.<App>
<Form data="{{ name: 'Joe', age: 43 }}">
<event name="submit">
<APICall
url="/api/contacts"
method="POST"
body="{$param}" />
</event>
<FormItem bindTo="name" label="name" />
<FormItem bindTo="age" label="age" />
</Form>
</App>
Form in ModalDialog
ModalDialog supports
Form
as a first-class citizen component. When a Form
nests directly in a ModalDialog
, the dialog's button row is replaced with the form's own button row. When form submission is successful, the dialog closes.