Skip to main content
Version: Previous

Foundation Forms - advanced customisation

lerna TypeScript

tip

You can click to view our API documentation in a separate area.

You can control the display of information using layout renderers. Typically a renderer requires an accompanying schema to specify the layout, but in some cases there are defaults.

  • vertical and horizontal layouts
  • arrays (dynamic form)
  • groups of related information
  • steps

Layout renderers

Vertical and horizontal

VerticalLayout has a default layout, which is used if no uiSchema is specified. This arranges the control elements vertically.

const VerticalUISchema = {
type: 'VerticalLayout',
elements: [
...
],
};

If you specify a uiSchema, you can control the layout detail. This example arranges the control elements in two columns vertically.

const VerticalColumnsUISchema = {
type: 'LayoutVertical2Columns',
elements: [
...
],
};

The example below specifies a uiSchema that controls the elements horizontally.

const horizontalUISchema = {
type: 'HorizontalLayout',
elements: [
...
],
};

Arrays (dynamic forms)

An array layout enables you to create a dynamic form with the ability to add, for example, multiple users.

For this, you need both a uiSchema and a jsonSchema.

const arrayUISchema = {
type: "VerticalLayout",
elements: [
{
type: "Control",
scope: "#/properties/users",
options: {
childUiSchema: {
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/firstname",
label: "First Name",
},
{
type: "Control",
scope: "#/properties/lastname",
label: "Last Name",
},
{
type: "Control",
scope: "#/properties/email",
label: "Email",
},
],
},
},
},
],
};
const arrayJsonSchema = {
type: "object",
properties: {
users: {
type: "array",
items: {
type: "object",
title: "Users",
properties: {
firstname: {
type: "string",
},
lastname: {
type: "string",
},
email: {
type: "string",
format: "email",
},
},
},
},
},
};

Grouping information

The Categorization layout enables you to create more complex forms that are divided into appropriate categories (for example, personal information and address), which are displayed in separate tabs.

const categoryUISchema = {
type: "Categorization",
elements: [
{
type: "Control",
scope: "#/properties/basic",
label: "Personal information",
options: {
childElements: [
{
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/firstName",
},
{
type: "Control",
scope: "#/properties/secondName",
},
],
},
],
},
},
{
type: "Control",
label: "Address",
scope: "#/properties/address",
options: {
childElements: [
{
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/address/properties/street",
},
{
type: "Control",
scope: "#/properties/address/properties/streetNumber",
},
],
},
],
},
},
],
};

Group layout divides forms into groups. These are visible on the same tab, but they are separated from each other by their own labels (unlike Categorization, which displays groups in separate tabs).

const groupUISchema = {
type: "VerticalLayout",
elements: [
{
type: "Group",
label: "Person",
scope: "#/properties/person",
options: {
childElements: [
{
type: "LayoutVertical2Columns",
elements: [
{
type: "Control",
label: "First Name",
scope: "#/properties/person/properties/firstName",
},
{
type: "Control",
scope: "#/properties/person/properties/lastName",
},
],
},
],
},
},
{
type: "Group",
label: "Address",
scope: "#/properties/address/",
options: {
childElements: [
{
type: "VerticalLayout",
elements: [
{
type: "Control",
scope: "#/properties/person/properties/shippingAddress",
},
{
type: "Control",
scope: "#/properties/address/properties/street",
},
],
},
],
},
},
],
};

Stepper layout enables you to create complex forms that are divided into separate steps.

For this, you need both a uiSchema and a jsonSchema so that validation and data saving can work properly.

info

Remember to add a hide-submit-button attribute to foundation-forms, because in this case, submit is built directly into stepper-layout.

const uiSchemaStepper = {
type: 'Stepper',
elements: [
{
type: 'Control',
scope: '#/properties/person',
label: 'Entity',
options: {
childElements: [
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/person/properties/firstName',
},
{
type: 'Control',
scope: '#/properties/person/properties/secondName',
},
],
},
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/person/properties/birthDate',
},
{
type: 'Control',
scope: '#/properties/person/properties/nationality',
},
],
},
],
},
},
{
type: 'Control',
label: 'Doc',
scope: '#/properties/address',
options: {
childElements: [
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/address/properties/street',
},
{
type: 'Control',
scope: '#/properties/address/properties/streetNumber',
},
],
},
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/address/properties/city',
},
{
type: 'Control',
scope: '#/properties/address/properties/postalCode',
},
],
},
],
},
},
{
type: 'Control',
label: 'Primary doc',
scope: '#/properties/vegetarianOptions',
options: {
childElements: [
{
type: 'VerticalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/vegetarianOptions/properties/favoriteVegetable',
},
{
type: 'Control',
scope: '#/properties/vegetarianOptions/properties/otherFavoriteVegetable',
},
],
},
],
},
},
],
};
const jsonSchemaStepper = {
type: 'object',
properties: {
person: {
type: 'object',
properties: {
firstName: {
type: 'string',
minLength: 3,
description: 'Please enter your first name',
},
secondName: {
type: 'string',
minLength: 3,
description: 'Please enter your second name',
},
birthDate: {
type: 'string',
format: 'date',
description: 'Please enter your birth date.',
},
nationality: {
type: 'string',
description: 'Please enter your nationality.',
},
},
required: ['firstName', 'secondName'],
},
address: {
type: 'object',
properties: {
street: {
type: 'string',
},
streetNumber: {
type: 'string',
},
city: {
type: 'string',
},
postalCode: {
type: 'string',
maxLength: 5,
},
},
required: ['postalCode'],
},
vegetarianOptions: {
type: 'object',
properties: {
favoriteVegetable: {
type: 'string',
enum: ['Tomato', 'Potato', 'Salad', 'Aubergine', 'Cucumber', 'Other'],
},
otherFavoriteVegetable: {
type: 'string',
},
},
required: ['otherFavoriteVegetable'],
},
},
};

Control renderers

Most renderers are defined directly in the jsonSchema that comes from the server, but you can also add these in your uiSchema.

String renderer is the default renderer, which creates a text-field.

const stringJsonSchema = {
type: "object",
properties: {
ISSUER_NAME: {
type: "string",
minLength: 3,
description: "kotlin.String",
},
USER: {
type: "string",
description: "kotlin.String",
},
MAIN_CONTACT: {
type: "string",
pattern: "^[\\+]?[(]?[0-9]{3}[)]?[-\\s\\.]?[0-9]{3}[-\\s\\.]?[0-9]{4,6}$",
description: "kotlin.String",
},
},
};

The number renderer creates a number-field.

const numberJsonSchema = {
type: "object",
properties: {
PRICE: {
type: "number",
description: "kotlin.Double",
},
},
};

The boolean renderer creates a checkbox-field.

const booleanJsonSchema = {
type: "object",
properties: {
vegetarian: {
type: "boolean",
},
},
};

The connected multiselect renderer creates a multiselect component with options from datasource.

const connectedMultiselectUISchema = {
type: "HorizontalLayout",
elements: [
{
type: 'Control',
label: 'Rights',
scope: '#/properties/RIGHT_CODES',
options: {
allOptionsResourceName: 'RIGHT',
valueField: 'CODE',
labelField: 'CODE',
},
},
{
type: 'Control',
label: 'Users',
scope: '#/properties/USER_NAMES',
options: {
allOptionsResourceName: 'USER',
valueField: 'USER_NAME',
labelField: 'USER_NAME',
},
},
],
};

Connected Select renderer is a select renderer that creates a select component with options.

const connectedSelectUISchema = {
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/COUNTERPARTY_ID",
options: {
data: CounterpartyOptions,
valueField: "value",
labelField: "name",
},
label: "Counterparty",
},
{
type: "Control",
scope: "#/properties/INSTRUMENT_ID",
options: {
data: InstrumentOptions,
valueField: "value",
labelField: "name",
},
label: "Instrument",
},
],
};

The date renderer creates a date-field.

const dateJsonSchema = {
type: "object",
properties: {
tradeDate: {
type: "string",
description: "org.joda.time.DateTime",
},
},
};

Specific renderers for filters

The filter date renderer creates two date-fields with minimum and maximum value.

const dateJsonSchema = {
type: "object",
properties: {
tradeDate: {
type: "string",
description: "org.joda.time.DateTime",
},
},
};

The filter number renderer creates two number-fields with minimum and maximum value.

const numberJsonSchema = {
type: "object",
properties: {
PRICE: {
type: "number",
description: "kotlin.Double",
},
},
};

License

Note: this project provides front-end dependencies and uses licensed components listed in the next section; thus, licenses for those components are required during development. Contact Genesis Global for more details.

Licensed components

Genesis low-code platform