elementLabelProp displays const instead of title of oneOfs (MaterialRenderer)

I have an array with complex items. These array items are actually two strings that are selected from onOfs.

Example (also on Stackblitz):

const schema = {
  $schema: 'http://json-schema.org/schema#',
  $id: 'ObjectsWithOneOfInArrayExample',
  type: 'object',
  properties: {
    myArray: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          letter: {
            type: 'string',
            oneOf: [
              {
                const: 'A',
                title: 'A is the first letter in the English alphabet',
              },
              {
                const: 'B',
                title: 'B is the second letter in the English alphabet',
              },
            ],
          },
          romanNumber: {
            type: 'string',
            oneOf: [
              { const: 'I', title: 'I is the first roman number' },
              { const: 'II', title: 'II is the second roman number' },
            ],
          },
        },
      },
    },
  },
};

My uischema looks like follows:

const uischema = {
  type: 'Group',
  elements: [
    {
      type: 'Control',
      scope: '#/properties/myArray',
      options: {
        elementLabelProp: 'letter', // actually displays 'letter/const' but 'letter/title' would be better here
        detail: {
          type: 'HorizontalLayout',
          elements: [
            {
              type: 'Control',
              scope: '#/properties/letter',
            },
            {
              type: 'Control',
              scope: '#/properties/romanNumber',
            },
          ],
        },
      },
    },
  ],
};

which renders to:

As you can see, elementLabelProp targets the const component in letter. Technically, this makes sense as this is acutally the value for the letter in the data. However, in the UI it would be better if the label for a collapsed array item would be the title in the oneOf. Is that possible (without rewriting the whole renderer)?

Maybe this is somewhat related to this topic.

Hi @rwoerzbe,

Sadly the elementLabelProp concept is not generic enough to achieve the desired behavior as it’s designed to directly access the actual data. To adapt this in JSON Forms we must come up with a more powerful concept.

If you don’t care about the structure of your data or a willing to add transformers, then you could adapt your JSON Schema to:

  • store the title alongside the value in the data. In that case elementLabelProp could refer to the stored title in the data, e.g. oneOf: [ { title: 'ABC', const: { title: 'ABC', value: 'A' }} ]. Obviously that is a rather dirty hack
  • Only use the title in your JSON Schema, e.g. enum: ["A is the...", "B is the..."]. Then transform the stored titles into their actual values as a step inbetween JSON Forms and your application.

The clean solution is to fix this with a custom renderer. In this case you would need to copy the MaterialArrayLayout and write and use a new binding for the ExpandPanelRenderer in which you can add the behavior you like to see. The newly wrapped CustomExpandPanelRenderer can then be used in your MaterialArrayLayout. Then register this as a custom renderer with a higher priority as the existing MaterialArrayLayout and your’e done.

Now taking a step back I would like to actually recommend instead is just doing the same solution as in the linked topic. There is no reason to not show these two attributes directly within a table. This not only renders a cleaner UI as the expandable sections are still overkill for only two attributes, it also avoids the elementLabelProp issues. All you need to do is to adapt the tester in the linked topic to also recognize the structure with a shallow object and multiple oneOfs as properties.

1 Like

Thanks alot. I opted for latter solution and made the tester match my specific schema element with the two oneOf properties. Now it renders as a table.

1 Like