Extension

The extension component can be used to write your own component using HTML and JavaScript.

Properties

The form definition can include the following properties to control how the Extension component behaves. Each property corresponds to a the name of a JavaScript function that is invoked at a specific stage in the component’s lifecycle. These functions correspond to those used when creating Form.io custom components. It is not necessary to implement every function. If a function is not provided, the component will fall back to its default behavior. However, renderFn and attachFn are typically required to get a functioning component.

NameDescriptionFunction signature
constructorFnCalled when the component has been instantiated. This is useful to define default instance variable values.constructor(component, options, data)
initFnCalled immediately after the component has been instantiated to initialize the component.init()
renderFnThis method is used to render a component as an HTML string. It must return a valid HTML string.render(): string
attachFnThe attach method is called after "render" which takes the rendered contents from the render method (which are by this point already added to the DOM), and then "attach" this component logic to that html. This is where you would load any references within your templates (which use the "ref" attribute) to assign them to the "this.refs" component variable.attach(element): Promise
detachFnCalled when the component has been detached. This is where you would destroy any other instance variables to free up memory. Any event registered with "addEventListener" will automatically be detached so no need to remove them here.detach(): Promise
destroyFnCalled when the component has been completely "destroyed" or removed form the renderer.destroy(): Promise
normalizeValueFnA very useful method that will take the values being passed into this component and convert them into the "standard" or normalized value. For example, this could be used to convert a string into a boolean, or even a Date type.normalizeValue(value, flags = {}): value
getValueFnReturns the value of the "view" data for this component.getValue(): value
setValueFnSets the value of both the data and view of the component (such as setting the <input> value to the correct value of the data). This is most commonly used externally to set the value and also see that value show up in the view of the component. If you wish to only set the data of the component, like when you are responding to an HTML input event, then updateValue should be used instead since it only sets the data value of the component and not the view.setValue(value, flags = {}): Boolean
updateValueFnSimilar to setValue, except this does NOT update the "view" but only updates the data model of the component.updateValue(value, flags = {}): Boolean

Example

This example demonstrates a custom numeric input component integrated into a Simian application, combining:

  • Python backend logic
  • Custom JavaScript frontend extension
  • JSON-based form definition

The application renders a form with:

  • A custom counter component (with + and − buttons)
  • A button that triggers a backend event

Extension example

When the button is clicked, the current value of the custom component is sent to the backend, printed, and then reset to 0.

Python Backend

def gui_init(meta_data: dict) -> dict:
    form = Form(from_file=__file__)
    return {"form": form}
  • Loads the JSON form definition from the file
  • Creates a Form instance
  • Returns it to the frontend
def gui_event(meta_data: dict, payload: dict) -> dict:
    value, _ = utils.getSubmissionData(payload, "custom")
    print(value)

    newValue = 0
    utils.setSubmissionData(payload, "custom", newValue)

    return payload

Handles events from the front-end, in this case the button click:

  • Retrieves the value of the "custom" field
  • Prints it to the console
  • Sets the value to 0
  • Returns the updated payload

Custom JavaScript

The custom JavaScript code defines a custom Extension component.

function customRender() {
    let value = this.dataValue;
    return `<div>
        <button ... ref="minus">
            <i class="bi bi-dash-square"></i>
        </button>
        <input type="number" ref="number" value=${value} />
        <button ... ref="plus">
            <i class="bi bi-plus-square"></i>
        </button>
    </div>`;
}

Renders the following HTML elements:

  • ➖ Minus button
  • 🔢 Numeric input field, the value is set to the submission data
  • ➕ Plus button
async function customAttach(element) {
    this.loadRefs(element, {
        minus: 'single',
        number: 'single',
        plus: 'single'
    });

    this.refs.minus.addEventListener('click', () =>
        this.setValue(this.getValue() - 1)
    );

    this.refs.number.addEventListener('input', (event) =>
        this.setValue(event.target.valueAsNumber)
    );

    this.refs.plus.addEventListener('click', () =>
        this.setValue(this.getValue() + 1)
    );
}

The customAttach function loads the references defined in the customRender function (ref="..."), and attaches listeners to the elements.

  • Clicking ➖ decreases the value by one
  • Clicking ➕ increases the value by one
  • Typing updates the value in the data model
function customGetValue() {
    return this.refs.number.valueAsNumber;
}
  • Returns the numeric value of the input element.
function customSetValue(value, flags) {
    this.refs.number.value = value;
}
  • Updates the input field.

JSON Form Definition

{
    "components": [
        {
            "label": "Custom",
            "type": "extension",
            "key": "custom",
            "renderFn": "customRender",
            "attachFn": "customAttach",
            "getValueFn": "customGetValue",
            "setValueFn": "customSetValue",
            "defaultValue": 100
        },
        {
            "label": "Click",
            "type": "button",
            "key": "btn",
            "action": "event",
            "event": "click"
        }
    ]
}

The first component is the custom Extension component:

  • Type: "extension"
  • Default value: 100
  • Defines which custom JavaScript functions to use to specify its behavior

The second component is a Button:

  • Type: "button"
  • Action: "event"
  • Event name: "click"
  • When clicked it triggers gui_event in Python.