Rendering Images Dynamically

Hi,
I am trying to render images dynamically using a custom renderer.

If I use "type": "Group" in UI Schema and "oneOf" in Schema, when I switch the tabs and go to the next form, the data in the previous form is cleared (as can be seen in the image below), which is what I need, but the problem is that I cannot render different images for different tabs because in the UI Schema, I can have only one image for the whole group.


Is there a solution so that I can render one image if the tab “DESCRIPTION” is active, and render another image if the tab “DEPARTMENT” is active?
I checked the custom react hook useJsonForms(), but it does not provide a state for the active tab. Does JsonForms provide other custom hooks or is there a way that I can keep track of the active tab?

// UI Schema

{
    "type": "VerticalLayout",
    "elements": [
        {
            "type": "Group",
            "label": "Person",
            "elements": [
                {
                    "type": "Control",
                    "scope": "#/properties/person"
                }
            ]
        }
    ]
}

// Schema

{
    "type": "object",
    "properties": {
        "person": {
            "oneOf": [
                {
                    "oneOf": [
                        {
                            "$ref": "#/definitions/description",
                            "title": "description"
                        },
                        {
                            "$ref": "#/definitions/department",
                            "title": "department"
                        }
                    ],
                    "title": "job"
                },
                {
                    "oneOf": [
                        {
                            "$ref": "#/definitions/name",
                            "title": "name"
                        }
                    ],
                    "title": "Info"
                }
            ]
        }
    },
    "definitions": {
        "description": {
            "type": "object",
            "properties": {
                "description": {
                    "type": "string"
                }
            }
        },
        "department": {
            "type": "object",
            "properties": {
                "department": {
                    "type": "string"
                }
            }
        },
        "name": {
            "type": "object",
            "properties": {
                "name": {
                    "type": "string"
                }
            }
        }
    }
}

I tried another solution as well. Instead of using "type": "Group", I used "type": "Categorization",. This way, I can render different images for different tabs, but the problem is that after changing the tab, the data from the previous form persists and makes problem for form submission.

// UI Schema

{
  "type": "Categorization",
  "elements": [
    {
      "type": "Category",
      "label": "Description",
      "elements": [
        {
          "type": "Control",
          "scope": "#/properties/description/properties/description"
        }
      ]
    },
    {
      "type": "Category",
      "label": "Department",
      "elements": [
        {
          "type": "Control",
          "scope": "#/properties/department/properties/department"
        }
      ]
    },
    {
      "type": "Category",
      "label": "Name",
      "elements": [
        {
          "type": "HorizontalLayout",
          "elements": [
            {
              "type": "Control",
              "scope": "#/properties/firstName"
            },
            {
              "type": "Control",
              "scope": "#/properties/secondName"
            }
          ]
        },
        {
          "type": "HorizontalLayout",
          "elements": [
            {
              "type": "Control",
              "scope": "#/properties/birthDate"
            },
            {
              "type": "Control",
              "scope": "#/properties/nationality"
            }
          ]
        }
      ]
    }
  ]
}

// Schema

{
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "secondName": {
      "type": "string"
    },
    "birthDate": {
      "type": "string",
      "format": "date"
    },
    "nationality": {
      "type": "string",
      "enum": [
        "IT",
        "JP",
        "US",
        "Other"
      ]
    },
    "description": {
      "type": "object",
      "properties": {
        "description": {
          "type": "string"
        }
      }
    },
    "department": {
      "type": "object",
      "properties": {
        "department": {
          "type": "string"
        }
      }
    }
  }
}

I need a dynamic solution that works with similar forms but with different input fields, so I cannot hardcode the field names in the custom renderer.

Hi @farshad

No, unfortunately JSON Forms does not keep track of active tabs in its state. It is only tracked by the respective renderers.

You could use React contexts to pass a callback from your custom group renderer to your custom oneOf renderer. The oneOf renderer then calls the callback on tab change and the group renderer can react to this, i.e. update the image.

If you want to inject configuration to your custom renderer (i.e. relevant properties for the image), you can use the UI Schema element’s options property and add additional data
E.g. You could have a group element like this for your custom renderer:

{
  "type": "Group",
  "elements": [
    // your elements in the group
  ],
  "options": {
    "customImageConfig": {
      // add config you need
    }
  }
}

In your group renderer, you can then access the config like this with uischema.options.customImageConfig. With uischema coming for the renderer’s props.

Hope that helps and best regards,
Lucas

Thanks lucas. I have 2 more questions.

  1. I created the oneOf renderer using rankWith(4, isOneOfControl) and the GroupRenderer as a react component. I wrapped the JsonForm component by the GroupRenderer so that I could run both renderers at the same time.
    Is there a way to remove the GroupRenderer from the return statement and add it to the renderers array so that both renderers are executed at the same time?
const renderers = [
  ...materialRenderers,
  { tester: rankWith(4, isOneOfControl), renderer: OneOfRenderer }
  //{how to run two renderers at the same time?}
];

return (
    <ContextProvider>
      <GroupRenderer>
        <JsonForms
          schema={schema}
          uischema={uischema}
          data={data}
          renderers={renderers}
        />
      </GroupRenderer>
    </ContextProvider>
  );
  1. If in the schema, the first oneOf points to input A and the second oneOf points to input B, is it possible to first render input B and then input A, without changing the order in the schema?

Hi @farshad

  1. I created the oneOf renderer using rankWith(4, isOneOfControl) and the GroupRenderer as a react component. I wrapped the JsonForm component by the GroupRenderer so that I could run both renderers at the same time.
    Is there a way to remove the GroupRenderer from the return statement and add it to the renderers array so that both renderers are executed at the same time?

Yes, you can just add another tester for your GroupRenderer to the renderers array.
If you want to use your group renderer for all groups, just do:

const renderers = [
  ...materialRenderers,
  { tester: rankWith(4, isOneOfControl), renderer: OneOfRenderer },
  { tester: rankWith(4, uiTypeIs('Group')), renderer: GroupRenderer }
];

If you only want to use your group renderer for UI Schema element that have a certain option (i.e. the customImageConfig mentioned in my first answer), you can do something like this

const renderers = [
  ...materialRenderers,
  { tester: rankWith(4, isOneOfControl), renderer: OneOfRenderer },
  { tester: and(uiTypeIs('Group'), hasOption('customImageConfig')), renderer: GroupRenderer }
];
  1. If in the schema, the first oneOf points to input A and the second oneOf points to input B, is it possible to first render input B and then input A, without changing the order in the schema?

Unfortunately, this is not supported by the builtin oneOf renderer.

Best regards,
Lucas

Thanks a lot. Good luck.