Default values for readOnly fields

Hi,

I’m looking for a convenient way of providing default values (certainly for required or readOnly properties/controls).

I first tried to add a Custom Renderer that has a tester to check if a default value was given. The renderer would just wrap the JSONFormsDispatch component to allow for all other renderers to be used. It would only change the data prop, based on the default option. This doesnt work, since the data prop is only being resolved afterwards by an HOC that will resolve the data from the root state (based on the path).

I then found answers of @sdirix to this thread and this one, but when delegating the defaultValue to AJV, the values are only being added on validation, and not when the from is initially rendered. Certainly for readOnly fields, this is problematic.

When going through the source code, I als found declarations like registerDefaultData in core, but this is nowhere being used in the source, so I can’t really derive how this should be used.

Could you please point me in the right direction how to achieve what I’m looking for.

My use case:
Admin users of the app that I’m building use an editor to draft and save schema/uischema files. These schema files are being published to other users to render the forms, so custom data can be added. Some fields in these custom data forms, are required fields but cannot be overridden by the end user. I thought this would be possible by instructing the admin users to add the readOnly attribute together with the default option. If this will not be possible, I need to merge the resulting data of the forms toegther with another data file containing all of the defaults. But this would require the admin users to author a different file containing all of the defaults, or I would need to construct this data file by parsing the uischema/schema files to look for all of the defaults.

Hi @Sewdn,

We perform a validation run on initialization, so default values will be there from the very beginning. If that doesn’t work for you then there are some other issues in place.

A quirk to be aware of is the following:

{
  "title": "Person",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "default": "John Doe"
    },
    "age": {
      "type": "integer",
      "minimum": 0
    },
    "address": {
      "type": "object",
      "properties": {
        "street": {
          "type": "string",
          "default": "example street"
        },
        "city": {
          "type": "string",
          "default": "example city"
        }
      }
    }
  }
}

If you hand over an empty data object (i.e. {}) to JSON Forms with a default-enabled AJV, the street and city defaults will not be applied. That’s the case because the address object does not exist. So for them to be applied, you also need to add a default for the address, i.e. "default": {}

Maybe that is the issue you are running into.

If not, then please post the example JSON Schema for which the default-enabled AJV does not work for you at initial rendering.

Hi @sdirix !

Thanks for your swift response.

The problem is the provided data prop indeed.
When providing an empty data object {} to the data prop, the defaults are indeed available after initialization. When providing undefined or null no defaults are being set. This seems to me as unexpected behaviour.

For the case of embedded objects (as you mentioned), this is more of a critical issue, since you need to know how to create an empty object for initialisation, based on the schema. This feels like the responsability of JSONForms to provide those empty initialisation objects when no data is given (otherwise schema parsing is duplicated).

Is their a helper function being exported to construct these empty objects, given the schema?

Thanks for your help!
Kind regards,
Pieter

@sdirix There is also a problem with the defaults when using a wrapping renderer.
This is the code of the Wrapper:

const EssentialRenderer = (props: ControlProps) => {
  const { uischema } = props;
  const classes = useStyles();
  return (
    <div className={classes.essential}>
      <JsonFormsDispatch {...props} renderers={renderers} />
    </div>
  );
};

export default withJsonFormsControlProps(EssentialRenderer);

export const isEssentialControl = and(
  uiTypeIs('Control'),
  (uischema) => !uischema.hasOwnProperty('essentialVisited'),
  schemaMatches((schema) => !isEmpty(schema) && schema.hasOwnProperty('isEssential')),
);

export const tester = rankWith(1000, isEssentialControl);

The renderers are readded to the JsonFormDispatch to remove this Wrapper to avoid endless loops.

Now, when using the default attribute together with the isEssential attribute, the default is missing again (even when empty data object is being provided).
I noticed that also other attributes are being removed from the schema object.

When logging the schema prop in other renders that are being used later on, the rootSchema is logged, where I would expect the property schema to be forwarded.

(This issue is thus not related to the default property, but it looks like an issue of the JsonFormsDispatch component. Please tell me if you would like to open a ticket for this with a reproduction)

Ok, I got it.
It is because the Wrapper Renderer is being wrapped with the withJsonFormControlProps HOC.
When I remove this, it works as expected.

const EssentialRenderer = (props: ControlProps) => {
  const classes = useStyles();
  return (
    <div className={classes.essential}>
      <JsonFormsDispatch {...props} renderers={renderers} />
    </div>
  );
};

export default EssentialRenderer;

export const isEssentialControl = and(
  uiTypeIs('Control'),
  schemaMatches((schema) => !isEmpty(schema) && schema.hasOwnProperty('isEssential')),
);

export const tester = rankWith(1000, isEssentialControl);