Foundation Forms layouts
Foundation forms allows you to customise the layout of your forms.
The layouts must be one of UiSchemaElementType which are:
VerticalLayout- arranges the form elements vertically.HorizontalLayout- arranges the form elements horizontally.LayoutVertical2Columns- arranges the form elements in two columns vertically.Group- creates a form divided into separate groups, each with its own label.Categorization- creates a form divided into tabs, with only one tab (section) visible at a time.Array- creates a dynamic form for entering multiple instances of a data type.Stepper- creates a tabbed form with a sidebar showing form progress. Each step is completed separately by the user.
The layout of a form is configured in the type property of the UISchema
By default, the form uses VerticalLayout if no uiSchema is specified.
Examples
Vertical layout
Each item in the elements property is arranged vertically.
const uiSchema: UISchema = {
type: 'VerticalLayout',
elements: [
...
]
}
Horizontal layout
Each item in the elements property is arranged horizontally.
const uiSchema: UISchema = {
type: 'HorizontalLayout',
elements: [
...
]
}
Vertical two-column layout
Each item in the elements property is arranged vertically, with two items per line.
const uiSchema: UiSchema = {
type: 'LayoutVertical2Columns',
elements: [
...
]
}
Group
This is used for grouping form elements into separate blocks. Each section (block) has its own label.
In the example below, we have personal information split into basic and address sections. You can add as many groups as you want.
const uiSchema: UiSchema = {
type: 'VerticalLayout',
elements: [
{
type: 'Group',
scope: '#/properties/basic',
label: 'Personal information',
options: {
childElements: [
...
]
}
},
{
type: 'Group',
label: 'Address',
scope: '#/properties/address',
options: {
childElements: [
...
]
}
}
]
}
Categorization
This is used for grouping form elements into separate blocks. Each section (block) is shown in its own tab, and only one tab is visible at a time.
const uiSchema = {
type: 'Categorization',
elements: [
{
type: "Control",
scope: "#/properties/basic",
label: "Personal information",
options: {
childElements: [
{
type: "HorizontalLayout",
elements: [
...
]
}
]
}
},
{
type: "Control",
label: "Address",
scope: "#/properties/address",
options: {
childElements: [
{
type: "HorizontalLayout",
elements: [
...
],
},
],
},
},
]
}
Array
An array layout enables you to create a dynamic form with the ability to add multiple instances of your jsonSchema model. For this, you need both a uiSchema and a jsonSchema.
In this example, we are creating a form where the user can input multiple Swap Schedule items. Another case would be adding multiple users at once.
export const formsWithArraysJsonSchema: JSONSchema7 = {
properties: {
swapDates: {
type: 'array',
title: 'Swap Schedule',
items: {
type: 'object',
properties: {
date: {
type: 'number',
description: 'org.joda.time.DateTime',
},
amount: {
type: 'number'
},
notes: {
type: 'string'
}
},
required: [ 'date', 'number' ]
}
}
}
}
export const formsWithArraysUiSchema: UiSchema = {
type: 'VerticalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/swapDates',
options: {
childUiSchema: {
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/date",
label: "Date",
},
{
type: "Control",
scope: "#/properties/amount",
label: "Amount",
},
{
type: "Control",
scope: "#/properties/notes",
label: "Notes",
options: <StringRendererOptions>{
textarea: true,
},
},
],
},
},
}
]
}
To define a UiSchema for each array item form set the childUiSchema in the options config object for the array property.
Controlling delete functionality with canDelete
You can control whether individual array items can be deleted by providing a canDelete function in the array options. This function receives the array item data and its index, and should return a boolean indicating whether the delete button should be enabled for that item.
export const formsWithArraysUiSchema: UiSchema = {
type: 'VerticalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/swapDates',
options: <ArrayRendererOptions>{
childUiSchema: {
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/date",
label: "Date",
},
{
type: "Control",
scope: "#/properties/amount",
label: "Amount",
},
{
type: "Control",
scope: "#/properties/notes",
label: "Notes",
options: <StringRendererOptions>{
textarea: true,
},
},
],
},
canDelete: (item: any, index: number) => {
// Prevent deletion of the first item
return index > 0;
},
},
}
]
}
In this example, the canDelete function prevents deletion of the first array item (index 0) while allowing deletion of all other items. You can implement any logic based on the item's data or its position in the array.
Function signature:
canDelete?: (item: any, index: number) => boolean;
Parameters:
item: The data object for the current array itemindex: The zero-based index of the item in the array
Returns:
boolean:trueif the item can be deleted,falseif the delete button should be disabled
Example use cases:
- Prevent deletion of required items (e.g., at least one item must always exist)
- Conditionally allow deletion based on item properties (e.g., only allow deletion of draft items)
- Prevent deletion based on business rules (e.g., items that have been processed cannot be deleted)
Stepper
Stepper layout enables you to create more complex forms that can be divided into appropriate groups. Each group is a step that the user will have to complete. This is a useful way of leading a user through complex processes.
Customisation is more complicated, because it needs a proper jsonSchema and uiSchema so that validation and data saving work properly.
In this example, we have a form with three steps - personal, address and account details. Each step must be valid according to the underlying json schema before the user can continue to the next section.
Remember to add a hide-submit-button attribute to foundation-forms, because in this case, submit is built directly into stepper-layout.
<foundation-form
hide-submit-button="true"
...
>
</foundation-form>
const uiSchemaStepper = {
type: 'Stepper',
elements: [
{
type: 'Control',
scope: '#/properties/person',
label: 'Personal',
options: {
childElements: [
{
type: 'HorizontalLayout',
elements: [
...
],
},
{
type: 'HorizontalLayout',
elements: [
...
],
},
],
},
},
{
type: 'Control',
label: 'Address',
scope: '#/properties/address',
options: {
childElements: [
{
type: 'HorizontalLayout',
elements: [
...
],
},
{
type: 'HorizontalLayout',
elements: [
...
],
},
],
},
},
{
type: 'Control',
label: 'Account details',
scope: '#/properties/accountDetails',
options: {
childElements: [
{
type: 'VerticalLayout',
elements: [
...
],
},
],
},
},
],
};
This creates a three-step form with each step validated individually.