Is it possible to bind a control to a property of a list item which has certain index?

Hi,
I want to create an additional control that attach to a list item of specified index. For example, the
property text of second item in the list.

{
  "properties": {
    "comments": {
      "type": "array",
      "title": "Comments",
      "items": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          }
        }
      }
    }
  }
}
{
  "type": "VerticalLayout",
  "elements": [
    {
      "type": "Control",
      "scope": "#/properties/comments"
    },
    {
      "type": "Control",
      "scope": "#/properties/comments/items/properties/text",
      "label": "SecondCommentText",
      "options": {
        "specifyPath": "comments.1.text"
      },
      "rule": {
        "effect": "SHOW",
        "condition": {
          "scope": "#/properties/comments",
          "schema": {
            "minItems": 2
          }
        }
      }
    }
  ]
}

Hi @meiweimuli,

At the moment this is not directly possible. The testers don’t get the current path handed over when matching, so they can’t decide based on the array index.

To workaround this issue you could register a custom renderer for strings in arrays and just delegate back to the normal string renderer in case it’s not the index you are interested in. Another workaround would be to register a custom renderer for the array table / layout, dispatch yourself and actually do hand over the path to the testers, for example as part of the tester context (available since 3.0.0-rc.0). Your testers could check for this path and then only trigger for the appropriate array index.

Note that if you’re using React Material and want to customize cells of the table renderer then you have to do this via the alternative cells renderer set which only exists for this use case.

I tried the second method and register a custom ArrayItem renderer. Thank you.

{
  "type": "VerticalLayout",
  "elements": [
    {
      "type": "Control",
      "scope": "#/properties/comments"
    },
    {
      "type": "ArrayItem",
      "scope": "#/properties/comments",
      "options": {
        "index": 2,
        "detail": {
          "type": "VerticalLayout",
          "elements": [
            {
              "type": "Control",
              "label": "SecondCommentText",
              "scope": "/properties/text"
            }
          ]
        }
      }
    }
  ]
}
<template>
  <div>
    <DispatchRenderer
      v-if="
        typeof control.uischema.options?.index === 'number' &&
        control.data?.length > control.uischema.options?.index
      "
      :schema="control.schema"
      :uischema="childUiSchema"
      :path="composePaths(control.path, `${control.uischema.options?.index}`)"
      :enabled="control.enabled"
      :renderers="control.renderers"
      :cells="control.cells" />
  </div>
</template>

<script setup lang="ts">
import { ControlElement, composePaths } from '@jsonforms/core'
import {
  rendererProps,
  useJsonFormsArrayControl,
  DispatchRenderer,
} from '@jsonforms/vue'
import { useElementArrayControl } from '../utils'

const props = defineProps({ ...rendererProps<ControlElement>() })

const { control, childUiSchema } = useElementArrayControl(
  useJsonFormsArrayControl(props)
)
</script>
1 Like