Skip to content

Custom Components API

Travis Tidwell edited this page Oct 19, 2019 · 5 revisions

The Form.io renderer allows for the creation of Custom components. These can be created by extending the base components within Form.io and then registering them within the core renderer. This can be done as follows.

Creating Custom Component

import Base from 'formiojs/components/_classes/component/Component';
import editForm from 'formiojs/components/table/Table.form'

export default class CheckMatrix extends Base {
  constructor(component, options, data) {
    super(component, options, data);
    this.foo = 'bar';
  }

  static schema() {
    return Base.schema({
      type: 'checkmatrix',
      numRows: 3,
      numCols: 3
    });
  }

  static builderInfo = {
    title: 'Check Matrix',
    group: 'basic',
    icon: 'fa fa-table',
    weight: 70,
    documentation: 'http://help.form.io/userguide/#table',
    schema: CheckMatrix.schema()
  }

  static editForm = editForm

  /**
   * Render returns an html string of the fully rendered component.
   *
   * @param children - If this class is extendended, the sub string is passed as children.
   * @returns {string}
   */
  render(children) {
    // To make this dynamic, we could call this.renderTemplate('templatename', {}).

    let tableClass = 'table ';
    ['striped', 'bordered', 'hover', 'condensed'].forEach((prop) => {
      if (this.component[prop]) {
        tableClass += `table-${prop} `;
      }
    });

    let content = '';

    for (let i = 0; i < this.component.numRows; i++) {
      let row = '<tr>';
      for (let j = 0; j < this.component.numCols; j++) {
        let cell = '<td>';

        cell += this.renderTemplate('input', {
          input: {
            type: 'input',
            ref: `${this.component.key}-${i}`,
            attr: {
              id: `${this.component.key}-${i}-${j}`,
              class: 'form-control',
              type: 'checkbox',
            }
          }
        });

        cell += '</td>';
        row += cell;
      }
      row += '</tr>';
      content += row;
    }

    // Calling super.render will wrap it html as a component.
    return super.render(`
<table class=${tableClass}>
  <tbody>
     ${content}
  </tbody>
</table>
    `);
  }

  /**
   * After the html string has been mounted into the dom, the dom element is returned here. Use refs to find specific
   * elements to attach functionality to.
   *
   * @param element
   * @returns {Promise}
   */
  attach(element) {
    const refs = {};

    for (let i = 0; i < this.component.numRows; i++) {
      refs[`${this.component.key}-${i}`] = 'multiple';
    }

    this.loadRefs(element, refs);

    this.checks = [];
    for (let i = 0; i < this.component.numRows; i++) {
      this.checks[i] = Array.prototype.slice.call(this.refs[`${this.component.key}-${i}`], 0);

      // Attach click events to each input in the row
      this.checks[i].forEach(input => {
        this.addEventListener(input, 'click', () => this.updateValue())
      });
    }

    // Allow basic component functionality to attach like field logic and tooltips.
    return super.attach(element);
  }

  /**
   * Get the value of the component from the dom elements.
   *
   * @returns {Array}
   */
  getValue() {
    var value = [];
    for (var rowIndex in this.checks) {
      var row = this.checks[rowIndex];
      value[rowIndex] = [];
      for (var colIndex in row) {
        var col = row[colIndex];
        value[rowIndex][colIndex] = !!col.checked;
      }
    }
    return value;
  }

  /**
   * Set the value of the component into the dom elements.
   *
   * @param value
   * @returns {boolean}
   */
  setValue(value) {
    if (!value) {
      return;
    }
    for (var rowIndex in this.checks) {
      var row = this.checks[rowIndex];
      if (!value[rowIndex]) {
        break;
      }
      for (var colIndex in row) {
        var col = row[colIndex];
        if (!value[rowIndex][colIndex]) {
          return false;
        }
        let checked = value[rowIndex][colIndex] ? 1 : 0;
        col.value = checked;
        col.checked = checked;
      }
    }
  }
}

Custom Edit Forms

The Edit Form is the form that you see when you drag-and-drop a component onto the Form, and the modal shows up with a form where you can configure the form. This edit form can be fully customized by providing a Form.io JSON form within the return of the editForm method as follows.

export default class CheckMatrix extends Base {
  static editForm() {
    return {
      components: [
        {
          type: 'textfield',
          key: 'label',
          label: 'Label'
        }
      ]
    };
  }
}
Clone this wiki locally