Foundation Forms
API Docs
foundation-forms
is a library for efficiently building complex forms and filters at scale.
Foundation forms is defined by using two schemata:
resourceName
/jsonSchema
defines the underlying data to be shown in the UI (objects, properties, and their types).uiSchema
defines how this data is rendered as a form, e.g. the order of controls, their visibility, and the layout.
Forms
Basic install
To enable this module in your application, follow the steps below.
- Add
@genesislcap/foundation-forms
as a dependency in your package.json file. Whenever you change the dependencies of your project, ensure you run the$ npm run bootstrap
command again. You can find more information in the package.json basics page.
{
...
"dependencies": {
"@genesislcap/foundation-forms": "latest"
},
...
}
Events
The foundation-layout component has 2 custom events:
-
@submit-success: This event is triggered after the user submits the form, and the server responds with an ack message.
-
@submit-failure: This event is triggered after the user submits the form, and the server responds with a nack message.
Method
The foundation-layout component has the following method:
- reset(): resets all fields contained in the form. It can be used after a successful submission, for example.
1. Register components
import { Form } from '@genesislcap/foundation-forms';
...
Form
...
2. Add component to the template
<foundation-form resourceName="EVENT_TRADE_INSERT"></foundation-form>
This should generate working form based on the JSON schema
for that endpoint.
DevTools console will output auto-generated UI schema
that you can use to configure the form.
3. Configure form using UI schema
The foundation-forms component enables you to customise the generated form using a UI schema. Note that it does not change how the message is requested or received from the server. It only changes how it is displayed.
const sampleUISchema = {
type: "VerticalLayout",
elements: [
{
type: "Control",
scope: "#/properties/QUANTITY",
label: "Quantity",
},
{
type: "Control",
scope: "#/properties/SIDE",
label: "Side",
},
],
};
<foundation-form resourceName="EVENT_TRADE_INSERT" :uischema=${() => sampleUISchema}></foundation-form>
4. Configure form using JSON schema (optional)
Instead of providing resourceName
, you can hard-code the JSON schema
on the client.
const sampleJsonSchema = {
type: 'object',
properties: {
ISSUER_NAME: {
type: 'string',
minLength: 3,
description: 'kotlin.String',
},
PRICE: {
type: 'number',
description: 'kotlin.Double',
},
MAIN_CONTACT: {
type: 'string',
pattern: '^[\\+]?[(]?[0-9]{3}[)]?[-\\s\\.]?[0-9]{3}[-\\s\\.]?[0-9]{4,6}$',
description: 'kotlin.String',
},
PASSWORD: {
type: 'string',
description: 'kotlin.String',
},
},
additionalProperties: false,
required: ['ISSUER_NAME', 'MAIN_CONTACT'],
};
const sampleUiSchema = {
type: 'VerticalLayout',
elements: [
{
type: 'Control',
label: 'Issuer Name',
scope: '#/properties/ISSUER_NAME',
},
{
type: 'Control',
label: 'Phone',
scope: '#/properties/MAIN_CONTACT',
},
{
type: 'Control',
label: 'Price',
scope: '#/properties/PRICE',
},
{
type: 'Control',
scope: '#/properties/COUNTERPARTY',
options: {
allOptionsResourceName: 'COUNTERPARTY',
valueField: 'COUNTERPARTY_ID',
labelField: 'COUNTERPARTY_ID',
datasourceConfig: {
request: {
COUNTERPARTY_ID: 'ACME',
},
},
},
},
{
type: 'Control',
label: 'Password',
scope: '#/properties/PASSWORD',
options: {
isPassword: true,
},
},
],
};
<foundation-form :jsonSchema=${() => sampleJsonSchema} :uischema=${() => sampleUISchema}></foundation-form>
Use this when you want to avoid fetching metadata from the server, but be aware that it could get out of sync if metadata changes on the server.
5. Pre-fill forms with data (optional)
Use the data
attribute, which allows you to pre-fill the form with ready-made information.
const sampleData = {
ISSUER_NAME: 'Some Issuer',
INVIS: 'Invisible value!',
USER: 'JohnDoe',
};
<foundation-form resourceName="EVENT_TRADE_INSERT" :uischema=${() => sampleUISchema} :data=${() => sampleData}></foundation-form>
Filters
Basic install
To enable this module in your application, follow the steps below.
- Add
@genesislcap/foundation-forms
as a dependency in your package.json file. Whenever you change the dependencies of your project, ensure you run the$ npm run bootstrap
command again. You can find more information in the package.json basics page.
{
...
"dependencies": {
"@genesislcap/foundation-forms": "latest"
},
...
}
1. Register components
import { Filters } from '@genesislcap/foundation-forms';
...
Filters
...
2. Add component to the template
<foundation-filters resourceName="ALL_TRADES"></foundation-filters>
This should generate a working form based on the JSON schema
for that endpoint.
The DevTools console will output an autogenerated UI schema
that you can use to configure the filters
3. Configure form using UI schema
const sampleUISchema = {
type: "VerticalLayout",
elements: [
{
type: "Control",
scope: "#/properties/QUANTITY",
label: "Quantity",
},
{
type: "Control",
scope: "#/properties/SIDE",
label: "Side",
},
],
};
<foundation-filters resourceName="ALL_TRADES" :uischema=${() => sampleUISchema}></foundation-filters>
4. Configure form using JSON schema (optional)
Instead of providing resourceName
, you can hard-code the JSON schema
on the client.
const sampleJsonSchema = {
type: 'object',
properties: {
INSTRUMENT_ID: {
type: 'string',
minLength: 3,
description: 'kotlin.String',
},
QUANTITY: {
type: 'number',
description: 'kotlin.Double',
},
},
};
const sampleUiSchema = {
type: 'VerticalLayout',
elements: [
{
type: 'Control',
label: 'Instrument ID',
scope: '#/properties/INSTRUMENT_ID',
},
{
type: 'Control',
label: 'Quantity',
scope: '#/properties/QUANTITY',
},
],
};
<foundation-filters :jsonSchema=${() => sampleJsonSchema} :uischema=${() => sampleUISchema}></foundation-filters>
Use this when you want to avoid fetching metadata from the server, but be aware that it could get out of sync if metadata changes on the server.
5. An example of synchronizing values with datasource criteria
<zero-card>
<foundation-filters
resourceName="ALL_USERS"
:value=${sync((x) => x.allUsersfilters)}>
</foundation-filters>
</zero-card>
<zero-grid-pro>
<grid-pro-genesis-datasource
resource-name="ALL_USERS"
criteria=${(x) => x.allUsersfilters}
></grid-pro-genesis-datasource>
</zero-grid-pro>
Advanced customisation
This section describes the default layout renderers and gives some examples.
1. VerticalLayout
This is the default layout that is defined if no uiSchema
is specified. This arranges our control elements vertically.
const VerticalUISchema = {
type: 'VerticalLayout',
elements: [
...
],
};
2. VerticalLayout - two columns
This arranges our control elements in 2 columns vertically.
const VerticalColumnsUISchema = {
type: 'LayoutVertical2Columns',
elements: [
...
],
};
3. HorizontalLayout
This arranges our control elements horizontally.
const horizontalUISchema = {
type: 'HorizontalLayout',
elements: [
...
],
};
4. Array Layout
Array Layout allows you to create a dynamic form with the ability to add, for example, multiple users.
It is more complicated when it comes to customisation because it needs proper jsonSchema
and uiSchema
.
const arrayUISchema = {
type: "VerticalLayout",
elements: [
{
type: "array",
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",
},
},
},
},
},
};
5. Categorization layout
Categorization layout allows you to create more complex forms that can be divided into appropriate categories (for example, personal information and address), which will be in separate tabs.
const categoryUISchema = {
type: "Categorization",
elements: [
{
type: "Category",
scope: "#/properties/basic",
label: "Personal information",
options: {
childElements: [
{
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/firstName",
},
{
type: "Control",
scope: "#/properties/secondName",
},
],
},
],
},
},
{
type: "Category",
label: "Address",
scope: "#/properties/address",
options: {
childElements: [
{
type: "HorizontalLayout",
elements: [
{
type: "Control",
scope: "#/properties/address/properties/street",
},
{
type: "Control",
scope: "#/properties/address/properties/streetNumber",
},
],
},
],
},
},
],
};
6. Group layout
Group layout is similar to Categorization layout; it divides forms into groups. These are visible on the same tab, but they are separated from each other by their own labels.
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",
},
],
},
],
},
},
],
};
Default control renderers and examples
Most renderers are defined directly in the jsonSchema
that comes from the server, but there are also those that you can add via uiSchema
.
1. String renderer
This 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",
},
},
};
2. Number renderer
The number renderer creates a number-field
.
const numberJsonSchema = {
type: "object",
properties: {
PRICE: {
type: "number",
description: "kotlin.Double",
},
},
};
3. Boolean renderer
The boolean renderer creates a checkbox-field
.
const booleanJsonSchema = {
type: "object",
properties: {
vegetarian: {
type: "boolean",
},
},
};
4. Connected Multiselect renderer
The 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',
},
},
],
};
5. Connected Select renderer
This 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",
},
],
};
6. Date renderer
The date renderer creates a date-field
.
const dateJsonSchema = {
type: "object",
properties: {
tradeDate: {
type: "string",
description: "org.joda.time.DateTime",
},
},
};
Filter renderers
There are specific renderers for filters. Here we look at each one and provide examples.
1. Filter date renderer
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",
},
},
};
2. Filter number renderer
The filter number renderer creates two number-fields
with minimum and maximum values.
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