Skip to content

formio/core

Repository files navigation

Form.io Core Data Processing Engine

This library is the core data processing engine behind the Form.io platform. It is a set of isomorphic APIs that allow for complex orchestration (e.g. calculated values, conditionally hidden components, complex logic, etc.) of JSON form and submission definitions.

Usage

@formio/core is available as an npm package. You can install it using the package manager of your choice:

# npm
npm install --save @formio/core

# yarn
yarn add @formio/core

Development

Processing form and submission data efficiently has two distinct requirements:

  1. A form- and data-aware traversal of the form JSON; and
  2. A set of processing functions to derive (and occasionally mutate) form state.

The first requirement is accomplished via the eachComponentData and eachComponentDataAsync functions, which traverse each form component JSON and provide a callback parameter by which to interact with the component and it's corresponding submission data parameter(s).

The second requirement is accomplished via "processors" (e.g. calculate, validate, or hideChildren) which are functions that, given an evaluation context, operate on, derive state from, and occasionally mutate the form state and submission values depending on the internal form logic, resulting in a scope object that contains the results of each processor keyed by component path.

To run a suite of processor functions on a form and a submission, the process family of functions take a form JSON definition, a submission JSON definition, and an array of processor function as an arguments (encapsulated as a context object which is passed through to each callback processor function).

import { processSync } from '@formio/core';

const form = {
    display: 'form',
    components: [
        {
            type: 'textfield',
            key: 'firstName',
            label: 'First Name',
            input: true,
        },
        {
            type: 'textfield',
            key: 'lastName',
            label: 'Last Name',
            input: true,
        },
        {
            type: 'button',
            key: 'submit',
            action: 'submit',
            label: 'Submit',
        },
    ],
};

const submission = {
    data: {
        firstName: 'John',
        lastName: 'Doe',
    },
};

const addExclamationSync = (context) => {
    const { component, data, scope, path, value } = context;

    if (!scope.addExclamation) scope.addExclamation = {};
    let newValue = `${value}!`;

    // The scope is a rolling "results" object that tracks which components have been operated on by which processor functions
    scope.addExclamation[path] = true;
    _.set(data, path, newValue);
    return;
};

// The context object is mutated depending on which component is being processed; after `processSync` it will contain the processed components and data
const context = {
    components: form.components,
    data: submission.data,
    processors: [{ processSync: addExclamationSync }],
    scope: {},
};

// The `process` family of functions returns the scope object
const resultScope = processSync(context);

console.assert(resultScope['addExclamation']?.firstName === true);
console.assert(resultScope['addExclamation']?.lastName === true);
console.assert(submission.data.firstName === 'John!');
console.assert(submission.data.lastName === 'Doe!');

Debugging

Debugging the Form.io Enterprise Server can be challenging because of the added complexity of the @formio/vm library (which sandboxes the @formio/core processors for safe execution of untrusted JavaScript on the server). Instructions on how to debug can be found here.

Experimental

This library contains experimental code (found in the src/experimental directory or via an import, e.g. import { Components } from @formio/core/experimental) that was designed to update and replace the core rendering engine behind the Form.io platform. It is a tiny (12k gzipped) rendering framework that allows for the rendering of complex components as well as managing the data models controlled by each component.

Usage

To use this experimental framework, you will first need to install the parent library into your application.

# npm
npm install --save @formio/core
# yarn
yarn add @formio/core

Next, you can create a new component as follows.

import { Components } from '@formio/core/experimental';
Components.addComponent({
    type: 'h3',
    template: (ctx) => `<h3>${ctx.component.header}</h3>`
});

And now this component will render using the following.

const header = Components.createComponent({
    type: 'h3',
    header: 'This is a test',
});
console.log(header.render());  // Outputs <h3>This is a test</h3>

You can also use this library by including it in your webpage scripts by including the following.

<script src="https://cdn.jsdelivr.net/npm/@formio/base@latest/dist/formio.core.min.js"></script>

After you do this, you can then do the following to create a Data Table in your website.

FormioCore.render(document.getElementById('data-table'), {
    type: 'datatable',
    key: 'customers',
    components: [
        {
            type: 'datavalue',
            key: 'firstName',
            label: 'First Name'
        },
        {
            type: 'datavalue',
            key: 'lastName',
            label: 'First Name'
        }
    ]
}, {}, {
    customers: [
        {firstName: 'Joe', lastName: 'Smith'},
        {firstName: 'Sally', lastName: 'Thompson'},
        {firstName: 'Mary', lastName: 'Bono'}
    ]
});