Advice on clearing down data on hiding a Group

Let’s say we have a whole Group that is required to be hidden based on a rule via a property value.

Can you please advise how you can go about clearing down the values in data of the controls in the group if they were completed and subsequently hidden?

Hi! JSON Forms currently has no built-in concept to change data based on rules. In general we try to avoid changing data just by rendering. Out of the box this is currently only supported indirectly by handing over a customized AJV to JSON Forms which has some support for adapting data while validating it.

If you would like to implement something like this there are multiple options going forward.

  • You could listen from the outside to data changes within JSON Forms and evaluate the same rule (manually or using AJV like we do in JSON Forms) and then adapt the data you hand over to JSON Forms yourself, or
  • You can add a custom group renderer which when visible is evaluated to false, iterates through all its children controls, resolves their data and sets them to undefined, or
  • To not restrict yourself to groups you could also register a full set of custom controls, each responsible on their own to set their own data to undefined when their visible is set to false. Note however that at the moment children of invisible layouts will not even be rendered so you need to adapt this too, or
  • If your use case is actually restricted to controls which point to objects (and are therefore by default rendered as a group), it’s sufficient to register a custom object renderer and just call handleChange(path, undefined) in them when visible is false.

I struggled with this issue for some time before finding a solution.
I remember finding a couple of threads here that spoke about this but i’ll leave my solution here and tag @sdirix for future references.

import {
  isControl,
  isVisible,
  JsonFormsCore,
  UISchemaElement,
} from '@jsonforms/core';
import { get, set } from 'lodash';

import { jsonFormAjv } from '../../../components/JsonForm/JsonForm';

interface ElementContainingElements extends UISchemaElement {
  elements: UISchemaElement[];
}

const hasElements = (
  uiElement: UISchemaElement,
): uiElement is ElementContainingElements => {
  return Object.hasOwn(uiElement, 'elements');
};

// a recursive function that check whether the uiElement of a json schema or any
// of it's parents is invisible and sets the data as undefined if so
export const removeInvisibleFormData = (
  uiElement: UISchemaElement,
  data: JsonFormsCore['data'],
  isParentVisible: boolean = true,
) => {
  const visible =
    isVisible(uiElement, data, '', jsonFormAjv) && isParentVisible;

  if (hasElements(uiElement)) {
    uiElement.elements.forEach((element) => {
      removeInvisibleFormData(element, data, visible);
    });
  }

  if (isControl(uiElement)) {
    const path = uiElement.scope
      .replaceAll('properties', '.')
      .split('/')
      .filter((el) => el.length > 1)
      .join('.');

    const elementData = get(data, path);
    if (!!elementData && !visible) {
      set(data, path, undefined);
    }
  }

  return data;
};

This will filter out the data that is not visible based on its scope.

Hi @lyzergic,

This looks good! Note that you don’t need to manually convert the scope to a path, instead you can use toDataPath (or toDataPathSegments for more control) which is exported from JSON Forms.

1 Like