Multiple Forms
There are use cases where multiple forms should be embedded into a single page. This section describes some of these in more detail.
Interlinked inputs
Withing the same form, inputs can be interlinked and interact with each other by using $ref
and setting the scope
property within the UI schema accordingly.
In the example below, the schema contains two entities, Person and Address. Each of them is rendered in their own group.
Whenever we change the shippingAddress
property of the person, it is subsequently updated in the address form and vice versa.
- Demo
- Schema
- UI Schema
- Data
{"type": "object","properties": {"person": {"title": "Person","type": "object","properties": {"firstName": {"type": "string"},"lastName": {"type": "string"},"age": {"description": "Age in years","type": "integer","minimum": 0},"shippingAddress": {"$ref": "#/properties/address/properties/addressId"}},"required": ["firstName","lastName"]},"address": {"title": "Order","type": "object","properties": {"addressId": {"type": "string","label": "Address Type","enum": ["Home Address 1","Home Address 2","Workplace"]},"street": {"type": "string"},"city": {"type": "string"},"zipCode": {"type": "string"}}}}}
{"type": "VerticalLayout","elements": [{"type": "Group","label": "Person","elements": [{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/person/properties/firstName"},{"type": "Control","scope": "#/properties/person/properties/lastName"}]},{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/person/properties/age"},{"type": "Control","label": "Address","scope": "#/properties/person/properties/shippingAddress"}]}]},{"type": "Group","label": "Address","elements": [{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/person/properties/shippingAddress"},{"type": "Control","scope": "#/properties/address/properties/street"}]},{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/address/properties/city"},{"type": "Control","scope": "#/properties/address/properties/zipCode"}]}]}]}
{}
The code for the example above looks as follows:
const schema = {
type: 'object',
properties: {
person: {
title: 'Person',
type: 'object',
properties: {
firstName: {
type: 'string',
},
lastName: {
type: 'string',
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0,
},
shippingAddress: {
$ref: '#/properties/address/properties/addressId',
},
},
required: ['firstName', 'lastName'],
},
address: {
title: 'Order',
type: 'object',
properties: {
addressId: {
type: 'string',
label: 'Address Type',
enum: ['Home Address 1', 'Home Address 2', 'Workplace'],
},
street: {
type: 'string',
},
city: {
type: 'string',
},
zipCode: {
type: 'string',
},
},
},
},
};
const uischema = {
type: 'VerticalLayout',
elements: [
{
type: 'Group',
label: 'Person',
elements: [
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/person/properties/firstName',
},
{
type: 'Control',
scope: '#/properties/person/properties/lastName',
},
],
},
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/person/properties/age',
},
{
type: 'Control',
label: 'Address',
scope: '#/properties/person/properties/shippingAddress',
},
],
},
],
},
{
type: 'Group',
label: 'Address',
elements: [
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/person/properties/shippingAddress',
},
{
type: 'Control',
scope: '#/properties/address/properties/street',
},
],
},
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/address/properties/city',
},
{
type: 'Control',
scope: '#/properties/address/properties/zipCode',
},
],
},
],
},
],
};
const InterLinkedForms = () => {
const [data, setData] = useState({});
return (
<JsonForms
data={data}
onChange={({ errors, data }) => setData(data)}
schema={schema}
uischema={uischema}
renderers={materialRenderers}
/>
);
};
Independent forms
There might be use cases where you have forms that do not have anything in common, so your forms are independent.
In such cases you use different JsonForms
components and pass the necessary props to each of them.
To illustrate, let's look again at the example from before, but this time the person
and address
schemas are not stored in any common parent schema.
- Demo
- Schema
- UI Schema
- Data
is a required property
is a required property
{"title": "Person","type": "object","properties": {"firstName": {"type": "string"},"lastName": {"type": "string"},"age": {"description": "Age in years","type": "integer","minimum": 0},"shippingAddress": {"type": "string"}},"required": ["firstName","lastName"]}
{"type": "Group","label": "Person","elements": [{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/firstName"},{"type": "Control","scope": "#/properties/lastName"}]},{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/age"},{"type": "Control","scope": "#/properties/shippingAddress"}]}]}
{}
- Demo
- Schema
- UI Schema
- Data
{"title": "Order","type": "object","properties": {"addressId": {"type": "string","label": "Address Type","enum": ["Home Address 1","Home Address 2","Workplace"]},"street": {"type": "string"},"city": {"type": "string"},"zipCode": {"type": "string"}}}
{"type": "Group","label": "Address","elements": [{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/addressId"},{"type": "Control","scope": "#/properties/street"}]},{"type": "HorizontalLayout","elements": [{"type": "Control","scope": "#/properties/city"},{"type": "Control","scope": "#/properties/zipCode"}]}]}
{}
The code for the above example looks as follows:
const schema = ... //see above
const personSchema = schema.person;
const personUISchema = {
type: 'Group',
label: 'Person',
elements: [
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/firstName',
},
{
type: 'Control',
scope: '#/properties/lastName',
},
],
},
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/age',
},
{
type: 'Control',
label: 'Address',
scope: '#/properties/shippingAddress',
},
],
},
],
};
const addressSchema = schema.address;
const addressUISchema = {
type: 'Group',
label: 'Address',
elements: [
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/addressId',
},
{
type: 'Control',
scope: '#/properties/street',
},
],
},
{
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/city',
},
{
type: 'Control',
scope: '#/properties/zipCode',
},
],
},
],
};
const IndependentForms = () => {
const [person, setPerson] = useState({});
const [address, setAddress] = useState({});
return (
<div>
<JsonForms
data={person}
onChange={({ errors, data }) => setPerson(data)}
schema={personSchema}
uischema={personUISchema}
renderers={materialRenderers}
/>
<JsonForms
data={address}
onChange={({ errors, data }) => setAddress(data)}
schema={addressSchema}
uischema={addressUISchema}
renderers={materialRenderers}
/>
</div>
);
};