Skip to main content

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.

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.

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>
);
};