widjet-json-form

1.2.1 • Public • Published

widjet-json-form Build Status codecov

A widget to generate forms using a json object descriptor

Install

npm install --save widjet-json-form

Usage

The json-form widget generates form fields in the target element by reading a json schema. Values can be specified for the form to populate the fields.

Two preferred ways are available to define the schema and values for a field:

Using Script Tags

Using script tags for schema and values offer the advantages of removing the characters escaping contraints of attributes and readability of indented code at the cost of a heavier markup and the need to have unique ids for every tags.

import widgets from 'widjet';
import 'widjet-json-form';

widgets('json-form', '[data-schema-source]', {on: 'load'});
<script id='schema' type='application/json'>
  {
    "title": "string",
    "content": "markdown",
    "publishedAt": "datetime"
  }
</script>
<script id='values' type='application/json'>
  {
    "title": "Here's a title",
    "content": "Then some content",
    "publishedAt": "2016-09-08T20:35:54.855Z"
  }
</script>
<div data-schema-source='schema'
     data-values-source='values'
     data-id='news-form'>
</div>

Using Data Attributes

Data attributes offers an easier setup for small forms that doesn't have a lot of settings or complex strings with special characters to escape. It's also more convenient when many forms are to be generated on a single page as it doesn't require to inject scripts with unique ids for each forms.

import widgets from 'widjet';
import 'widjet-json-form';

widgets('json-form', '[data-schema]', {on: 'load'});
<div data-schema='{"title": "string", "content": "markdown", "publishedAt": "datetime"}'
     data-values='{"title": "Here&quo;s a title", "content": "Then some content", "publishedAt": "2016-09-08T20:35:54.855Z"}'
     data-id='news-form'>
</div>

Form Schema

A form schema is an object whose keys represent the field names, and its values the field descriptors.

{
  "property": "type",

  "otherProperty": {
    "type": "type",
    "fieldSetting": "whatever"
  }
}

The simplest way to define a field is to create a pair "property": "type". In that case no other configuration will be available in the field template.

The other way is to define a pair "property": {object} where the object has a mandatory type key. Every other properties of that object will be passed to the field's template as its parameters, allowing for a finer setup of the field.

Here's an example of a more complete setup for a field of type integer:

{
  "integerField": {
    "type": "integer",
    "hint": "Hint for input of type `integer`. Hints support markdown.",
    "placeholder": "12345",
    "min": 0,
    "max": 1000,
    "step": 10,
    "default": 100,
    "required": true
  }
}

It's up to you to decide which parameters your field's input template will support. The only requirement is that the field config contains a type key.

For instance, here's what a Twig template for the field described above look like:

<label for='{{ id }}'>{{ attribute }}{% if not parameters.required %} <em> - Optional</em>{% endif %}</label>

{% if parameters.hint %}{% markdown %}{{ parameters.hint }}{% endmarkdown %}{% endif %}

<input type='number'
       id='{{ id }}'
       step='{{ parameters.step|default(1) }}'
       {% if parameters.min %}min='{{ parameters.min }}'{% endif %}
       {% if parameters.max %}max='{{ parameters.max }}'{% endif %}
       value='{{ value|default(parameters.default)|default }}'
       {% if parameters.placeholder %}placeholder="{{ parameters.placeholder }}"{% endif %}
       name='{{ name }}'
       {% if parameters.required %}required{% endif %}>
</input>

Templates

By default the widget will look for templates in the window.JST object, as it's one of the most common places to store javascript templates. All the templates are retrieved using a name such as json-form/<name>.

As with most JS templating engine, a template is expected to be a function that takes a parameters object and that returns a HTML string.

Note that the default field's renderer will look for a template according to the field's type. That means that if the type of the field is boolean, the field renderer will try to render the json-form/boolean with the parameters it received and then pass the result as the content parameter to the field template. Concretely, it means that you just have to create a template to allow a new type of input in the form without any other setup

The default template names being used by the widget are the following:

Name Path Description
form json-form/form

The wrapper of the whole form.

Parameters: content, id

field json-form/field

The wrapper for a form's field.

Parameters: content, id, name, type, value, attribute, attributePath, parameters

.
object json-form/object

If the field's type is object this template will be used as a wrapper for the whole sub-form instead of the field one.

Parameters: content, id, name, type, value, attribute, attributePath, parameters

.
array json-form/array

If the field's type is array this template will be used as a wrapper for all the items in the array instead of the field one.

Parameters: content, id, name, type, value, attribute, attributePath, parameters

.
arrayItem json-form/arrayItem

If the field's type is array, the sub-form for each item will be wrapped using this template.

Parameters: content, id, name, type, value, attribute, attributePath, parameters

.
<type> json-form/<type>

Each field types must have their own template.

Parameters: id, name, type, value, attribute, attributePath, parameters

.

Defining the templates in the window.JST object, or in another object, is up to you. The json-form widget doesn't provide any template to let you keep the full control of how forms are rendered.

Templates parameters

Name Description
id

The id of the element.

For a form template, the id is either the value provided in the data-id or an auto-incremented number id.

For field-related templates, the id is generated automatically using the id of its parent form and the attribute path. By default the id for a property age of the second item of a users array in a form with id company would be company-users-1-age. This can be changed through the fieldId widget setting.

type The type of the field as defined in the schema.
attribute The name of the current attribute.
attributePath An array with every components that form the path to the attribute, starting from the schema root. As an example, for the property age of the second item of the users array, the attribute path is ['users', 1, 'age']
name The input name, as generated by the fieldName function using the attribute path. By default the name for a property age of the second item of a users array would be users[1][age]. This can be changed through the fieldName widget setting.
value The value of the field, as provided in the data-values attribute.
parameters An object with the parameters of the field, as defined in the schema.
content Wrapper templates will receive the HTML content to include in that parameter.

Configuration

The widget accepts the following options:

Name Type Description
findTemplate function A function that takes the name of a template such as form or array and must returns a template function taking a parameters object and returning a HTML string.
fieldId function A function that takes the id of the form and an attribute path and that must returns the string id for the field.
fieldName function A function that takes an attribute path and that must returns the string name for its input.
schemaSourceAttribute string The attribute to use when looking for the schema script source. Defaults to data-schema-source. The value of the attribute should always be the id of a script tag
schemaAttribute string The attribute to use when looking for the schema on the target element. Defaults to data-schema.
valueSourceAttribute string The attribute to use when looking for the values script source. Defaults to data-values-source. The value of the attribute should always be the id of a script tag
valueAttribute string The attribute to use when looking for the values on the target element. Defaults to data-values.
idAttribute string The attribute to use when looking for the form's id on the target element. Defaults to data-id.
renderers array An array of custom renderers to be applied before the default ones. See more below about custom renderers.

The default renderers include renderers for object, array and the generic field renderer.

It's also possible to specify a template for a specific key without specifying a custom template finder function. The default findTemplate implementation will look for a property named <name>Template on the options object before looking in window.JST.

For instance if you just want to use a different template for the form while keeping using the defaults one for the other templates you can just do:

widgets('json-form', '[data-schema]', {
  on: 'load',
  formTemplate: ({content, id}) =>
    `<div class='form' id='${id}'>${content}</div>`,
});

Custom Renderers

A renderer is defined using two functions in an array. The first function is the predicate that defines whether the renderer applies to the passed-in type. The second function is a function returning function or a curried function that takes a renderObject function and the field's parameters and returns the field's HTML string.

The renderObject function that is passed to the render function is here so that you can write renderers that are able to include nested forms (as the default object and array renderers do).

When rendering a field, the render engine will loop through all the renderers and call their predicate function with the field's type as argument. The first renderer whose predicate function returns true will be used and its render function will then be called with the field's parameters object (containing the id, name, type, value, attribute, attributePath and parameters).

Using a Function Returning Function

const dummyRenderer = [
  type => type === 'boolean',
  renderObject => parameters =>
    `<input type="checkbox"
            value="1"
            name="${parameters.name}"
            id="${parameters.id}"
            ${parameters.value ? 'checked' : ''}>`,
];

widgets('json-form', '[data-schema]', {
  on: 'load',
  renderers: [dummyRenderer],
});

Using a Curried Function

const dummyRenderer = [
  type => type === 'boolean',
  curry2((renderObject, parameters) => {
    return `<input type="checkbox"
                   value="1"
                   name="${parameters.name}"
                   id="${parameters.id}"
                   ${parameters.value ? 'checked' : ''}>`;
  }),
];

widgets('json-form', '[data-schema]', {
  on: 'load',
  renderers: [dummyRenderer],
});

Package Sidebar

Install

npm i widjet-json-form

Weekly Downloads

3

Version

1.2.1

License

MIT

Unpacked Size

53.5 kB

Total Files

17

Last publish

Collaborators

  • abe33