jsonforms icon indicating copy to clipboard operation
jsonforms copied to clipboard

[question] Support for const?

Open tiloc opened this issue 4 years ago • 5 comments

Describe the bug I am not sure if this is a feature request or a bug. I am trying to pass constant strings from the schema into the JSON output of the form. According to the JSON Schema spec this should be possible by using the "const" keyword. Unfortunately, I cannot get this to work with JSON Forms.

To Reproduce Steps to reproduce the behavior:

  1. Create a schema with the const keyword
  2. Use it with the "seed" app
  3. The element is ignored, i.e. not shown in the form nor output to the JSON data

Expected behavior The constant data is passed through from the schema into the output.

Browser (please complete the following information): Chrome

Used Setup (please complete the following information):

  • Seed app from github repository

Additional context I am trying to use JSON Forms to generate valid FHIR healthcare resource output.

tiloc avatar May 29 '20 16:05 tiloc

Hi! Thanks for this report. const values were always a little bit out of scope for us as it doesn't really make sense to show an UI for them, however I can understand that you need them in some way in your data at some point.

Current state

  • We ignore const properties when generating a default ui schema, so it won't show up in the UI in that case.
  • When you hand in your own ui schema and point a control to the const property we'll render an enum where the single entry is the const value. The user still has to select that enum value as undefined is always another option.

Automatic generation within JSON Forms

  • You could customize your Ajv instance to generate default values when validating. For this to work the default property has to be specified in the JSON schema. The code could look like this: In the schema you define the default

      "myConst": {
        "const": "myvalue",
        "default": "myvalue"
      }
    

    then you customize your Ajv instance and hand it over to JSON Forms.

    import { createAjv } from '@jsonforms/core';
    const myAjv = createAjv({ useDefaults: true });
    // ....
      <JsonForms
          schema={schema}
          // etc.
          ajv={myAjv}
      />
    

    To note: In the Redux-less version of JSON Forms you'll get notified about the change in the data once the user does their very first change to any input. So if your form is already prefilled with only valid data (except the missing const) you might miss it. I think we should look into changing that in the future, but this is how it works at the moment. Alternatively you could just execute a single validation run before you give the data to JSON Forms (or after you got it back) and generate the default value this way.

  • If adding defaults to the JSON schema and/or Ajv customization is not the way you want to go but you want to still solve the problem within JSON Forms you could go with a custom renderer.

    However in your case the custom renderer could immediately set the desired value in the data once it's called and then either don't render anything when you want to hide the const or could for example render a disabled text field or label. I think that's an adequate solution, the only thing which is a bit "unclean" with this approach is to change data simply by rendering an UI.

    You can check the React seed to see how a custom renderer can be implemented and/or check this tutorial.

Let me know whether one of the approaches works for you. If you (or someone else :smile:) would like to contribute something for this use case: I think a const renderer (which is a bit nicer than an enum dropdown with a single entry) which allows to set/unset the value and optionally automatically sets the value (could be made configurable via the ui schema) would be something worthwhile.

In the meantime we could maybe change the default ui schema generation to also include a control for these const properties.

sdirix avatar May 29 '20 18:05 sdirix

A case where default is insufficient is where the form requests an array of heterogeneous objects and one wants to label each element by its type:

{
  "type": "array",
  "items": {
    "oneOf": [
      {
        "type": "object",
        "required": ["@type", "name"],
        "properties": {
          "@type": {
            "type": "string",
            "const": "Person"
          },
          "name": {
            "type": "string",
            "title": "Person's name"
          },
          "gender": {
            "type": "string",
            "enum": ["male", "female", "other", "not disclosed"]
          }
        }
      },
      {
        "type": "object",
        "required": ["@type", "name"],
        "properties": {
          "@type": {
            "type": "string",
            "const": "Organisation"
          },
          "name": {
            "type": "string",
            "title": "Org's name"
          }
        }
      }
    ]
  }
}

If one adds defaults and the user selects Organization and enters "name" only, then ajv will fill in "Person" as the default.

This seems to work for me:

import React from 'react';
import { withJsonFormsControlProps } from '@jsonforms/react';

export const ConstRenderer = withJsonFormsControlProps(({schema, handleChange, path}) => {
  React.useEffect(() => {
	handleChange(path, schema['const'])
  }, [schema, path]);
  return (<React.Fragment></React.Fragment>);
});

jnothman avatar May 19 '21 04:05 jnothman

@sdirix What if you're use-case is for the user to type in the const value? As a simple example, say you wanted the user to type "Confirm". This may not be a great UX design, but I have seen it.

My real use-case is I want to create a confirmation field where the user should enter the same value as entered in the previous field.

I tried to accomplish this with the schema below, but it creates a enum drop-down like you described in your previous comment.

{ "required": [ "username", "password", "confirmPassword" ], "properties": { "username": { "minLength": 3, "type": "string" }, "password": { "minLength": 6, "type": "string" }, "confirmPassword": { "const": { "$data": "1/password" }, "type": "string" } }, "type": "object" }

Source

dgerbe avatar Sep 16 '22 19:09 dgerbe

@jnothman thanks a thousand times, this solution helped us save a lot of time rewriting the array renderers.

@sdirix +1 for array types. Maybe there's a different solution to this, as different types of array items seem to be somewhat frequent.

Thanks for jsonforms in any case 😃

bastianwegge avatar Sep 28 '22 16:09 bastianwegge

We recently improved some of the const use cases: Whenever we create a new object in JSON Forms, we will look for default declarations and set them immediately. So when you are using const combined with a default set to the same value, JSON Forms will set your const automatically.

Note this only happens on user interactions in which the user triggers the creation of a new object. It's very useful for these "@type" use cases in which a const is used to identify the type of the object.

This is available since 3.2.0

sdirix avatar Apr 28 '24 21:04 sdirix