R

rjs_json_form

RenderJS gadget used for from generation from json schema definition

JSON Schema form generator RenderJS gadget

Usage

It's assumed that a developer is familiar with RenderJS gadget documentation (https://renderjs.nexedi.com/).

Gadget api is similar to any renderjs field api.

gadget.render({
  key: 'field_identificator',
  value: [1,2], // json_document
  schema: {
    type: 'array',
    items: {
      type: 'integer'
    }
  }
})

rendered

If schema_url is specified instead of schema property then schema_url is used for schema download.

Schema_url parameter should be specified for proper computation of an absolute url. If schema_url is omitted then the current gadget url is used as base_url.

gadget.getContent() can be used to get the current value of edited json document.

Parent gadget should have declared notification methods to catch validation status.

rJS(window)
  .allowPublicAcquisition("notifyValid", function (arr, scope) {
  })
  .allowPublicAcquisition("notifyInvalid", function (arr, scope) {
  })
  .allowPublicAcquisition("notifyChange", function (arr, scope) {
  });

In parent gadget resolveExternalReference() can be declared. resolveExternalReference() can be used to fetch schema from jio storage. Gadget runs resolveExternalReference() for fetching schema if schema_url starts with urn:jio: but path absolutisation works only for urn:jio:reference?. Example which demonstrates how a schema can be fetched from jio storage:

rJS(window)
  .allowPublicAcquisition("resolveExternalReference", function (arr) {
    var g = this,
      url = arr[0],
      reference,
      args;
    if (url.startsWith("urn:jio:reference?")) {
      reference = decodeURIComponent(url.replace("urn:jio:reference?", ""));
      args = {
        query: Query.objectToSearchText({
                   type: "complex",
                   operator: "AND",
                   query_list: [
                     {
                       key: "portal_type",
                       type: "simple",
                       value: "JSON Schema"
                     },
                     {
                       key: "reference",
                       type: "simple",
                       value: reference
                     }
                   ]
                 }),
        limit: [0, 1],
        select_list: [],
        sort_on: [["modification_date", "descending"]]
      };
      return g.jio_allDocs(args)
        .push(function (result) {
          return g.jio_getAttachment(result.data.rows[0].id, "data", {format: "json"});
        });
    }
  })

rendering rules

  • string json schema type is rendered as <input type="string">

for multi-line textfield can be used contentMediaType = "text/plain"

{
  "type": "string",
  "contentMediaType": "text/plain"
}

it is rendered as <textarea></textarea>

  • number/integer json schema type is rendered as <input type="number">

for integer step=1 is set by default

this step can be changed by using multipleOf property

html5 attributes min/max is used to limit the value

  • enum is rendered as a select
{"enum": ["Street", "Avenue", "Boulevard"]}

is rendered as select :

<select>
  <option value="Street">Street</option>
  <option value="Avenue">Avenue</option>
  <option value="Boulevard">Boulevard</option>
</select>
  • set of oneOf/anyOf const schema with titles render as select with titles:

json schema:

{"oneOf": [
  {
    "title": "Street - small road",
    "const": "Street"
  },
  {
    "title": "Avenue - larger road. This is the recommended option",
    "const": "Avenue"
  },
  {
    "title": "Boulevard",
    "const": "Boulevard"
  }
]}

json document:

"Avenue"

is rendered as select:

<select>
  <option value="Street">Street - small road</option>
  <option selected value="Avenue">Avenue - larger road. This is the recommended option</option>
  <option value="Boulevard">Boulevard</option>
</select>
  • json_document strongly react to render so:

schema

{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "title": "event",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "time": {"type": "integer"},
    "contact": {
      "anyOf": [
        {
          "title": "organisation",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "type": {"const": "organisation", "default": "organisation"},
            "title": {"type": "string"},
            "corporate_registration_code": {"type": "string"}
          }
        },
        {
          "title": "person",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "type": {"const": "person", "default": "person"},
            "title": {"type": "string"},
            "marital_status": {"enum": ["married", "divorced", "single"]}
          }
        }
      ]
    }
  }
}

if json_document = undefined then the it is rendered as:

event undefined

and clicking on selector:

event undefined_pressed

if json_document = {"contact":{"type":"person"}} then it's rendered as:

event_person

if json_document = {"contact":{"type":"organisation"}} then it's rendered as:

event_organisation

What is inside

gadget consists of three parts:

first part: Main gadget for form generation

jsonform.gadget.html

  • Expand schema - download reference, multiple anyOf andOf optimisation to one level anyOf which transformed in array of schemas. Resulting array of schemas (schema_arr) is used in gadget_json_generated_form_child.html to generate schema selector. Contains various schema optimisations which reduce user choice according to schema logic.

  • Using of parent_gadget.resolveExternalReference() to resolve "urn:jio:" url schema links - it allows to use gadget inside officejs application and work with schemas saved in jio storage. Relative links are correctly resolved too.

  • During expanding new json schema document (generated_schema) is created. It is based on original json schema and its references are substituted by downloaded/resolved schema parts. Gadget downloads and resolves references on demand, this allows to work with circular infinite schemas.

  • json_document validation by modified tv4 library using generated_schema.

  • control of two types of circular references:

    • Hard Circular Reference (HCR): when all fields in download stack are required and stack is circular. Creation of valid document based on schema which contains HCR is impossible. Gadget reports error and breaks infinite loop if HCR is detected.
    • Soft Circular Reference (SCR): when all property fields in circular stack have one schema selection. Property fields containing one variant schema rendered immediately. If SCR is found then gadget marks schema as circular and breaks infinite loop. Schema generation continues, next subform is rendered on user request.

second part: Gadget for recursive form generation

jsonform/gadget_json_generated_form_child.html

  • html5 form render on base of json schema definition.

  • recursive gadget declaration for rendering array items and optional properties.

  • gadget uses tv4 validator for guess: on base of which schema variant json document is valid. Chosen variant is used for form rendering.

  • calculate element by dataPath - field path inside json document. This is used to display validation errors in correct places.

third part: tv4

jsonform/tv4.js

modified version of original tv4.js. Difference from original:

  • support of draft7 except if-then-else

  • code which is responsible for references resolving and downloading is removed because tv4 does not work with circular schema.

known limitations

Render limitations described below are not critical because json document is rendered and correctly validated. Errors are alerted in case of document not valid.

string field rendering

  • minLength limiting realized as html5 regexp pattern attribute so it cannot be rendered simultaneously with pattern limiting.

number field rendering

  • multipleOf limiting realized as html5 step attribute so minimum limiting must be divisible by multipleOf without reminder for being rendered simultaneously with multipleOf.

  • exclusiveMaximum exclusiveMinimum limitation does not rendered

object field rendering

  • dependencies, maxProperties, minProperties rendering is not embodied

  • schema can contain patternProperties and regular properties. If regular properties match pattern from patternProperties then two schemas need merging which is not realized.

  • if schema has variation (anyOf or type is not specified) then schema selector is rendered. If json document already contains value in field which is related to specific schema variation then to change field type the field should be deleted and readded. In this case user has no hint that schema has more than one variation.

array field rendering

  • change items order currently unavailable

  • additionalItems partial support

  • maxItems, uniqueItems rendering is not supported

json schema limitation

  • modified tv4 validator support draft7 schema, except if-then-else.

reference resolver

  • id/$id is not used for change base url