Dynamic form - creating a new attribute renderer

In this tutorial we will describe how to create a new attribute renderer for a dynamic form attribute.

A new render will be used for a persistent type INT. Renderer define a new face PRIORITY-SELECT and an input will be represented as five radio buttons.

We will use AbstractFormAttributeRenderer component as superclass - contains some default method implementations. And define / override methods for getting / setting form value from radio buttons. Radio form component is not implemented in core components yet, so we are using simple radio inputs.

import React from 'react';
//
import { Advanced } from 'czechidm-core';
 
/**
 * Priority form value component. Doesn't support:
 * - multiple
 * - confidential
 * - placeholder
 * - helpBlock
 * - validation
 * - required
 */
export default class PrioritySelectFormAttributeRenderer extends Advanced.AbstractFormAttributeRenderer {
 
  constructor(props, context) {
    super(props, context);
    // prepare radio value
    this.state = {
      value: null
    };
  }
 
  /**
   * Fill form value field by persistent type from input value
   *
   * @param  {FormValue} formValue - form value
   * @param  {object} formComponent value
   * @return {FormValue}
   */
  fillFormValue(formValue, rawValue) {
    formValue.longValue = rawValue;
    // TODO: validations for numbers
    return formValue;
  }
 
  /**
   * Returns value to ipnut from given (persisted) form value
   *
   * @param  {FormValue} formValue
   * @return {object} value by persistent type
   */
  getInputValue(formValue) {
    return formValue.longValue;
  }
 
  /**
   * Filled form value
   *
   * @return {FormValue}
   */
  toFormValue() {
    const { values } = this.props;
    //
    return this.fillFormValue(this.prepareFormValue(values ? values[0] : null), this.state.value);
  }
 
  /**
   * Set value on radio is clicked (changed)
   *
   * @param {event} event
   */
  setValue(event) {
    this.setState({
      value: event.target.value
    });
  }
 
  renderSingleInput() {
    const { attribute, readOnly, values } = this.props;
    const singleValue = this.state.value || this.toInputValue(values);
    // create radio inputs
    const inputs = [];
    for (let i = 1; i <= PrioritySelectFormAttributeRenderer.RADIO_COUNT; i++) {
      inputs.push(
        <label className="radio-inline">
          <input
            name={ attribute.name }
            type="radio"
            readOnly={ readOnly || attribute.readonly }
            value={ i }
            defaultChecked={ singleValue === i }/> { i }
        </label>
      );
    }
    // raw bootstrap styles are used in this example (Basic radio component should be created instead)
    return (
      <div className="form-group">
        <label className="control-label">{ attribute.name }</label>
        <div className="radio" onChange={this.setValue.bind(this)}>
          { inputs }
        </div>
      </div>
    );
  }
}
 
PrioritySelectFormAttributeRenderer.RADIO_COUNT = 5;

Component is created, now we need to register her in component descriptor for start using her in application. New renderer will be available as new face type on form attribute detail page too, when persistent type INT will be selected.

Add component into module's component-descriptor.js:

...
module.exports = {
  ...
  'components': [
    ...
    {
      'id': 'priority-select-form-value',
      'type': 'form-attribute-renderer',
      'persistentType': 'INT',
      'faceType': 'PRIORITY-SELECT',
      'component': require('./src/components/PrioritySelectFormAttributeRenderer'),
      'labelKey': 'Priority radio select'
    }
    ...
  ]
};
...

We can select renderer for some dynamic form atribute with peristent type INT in form definitions agenda - e.g. we will add new attribute to default identity form definition:

On identity detail in tab more information, we can see, how result's look like:

Renderer name can be localized. See labelKey attribute in component definition. You can add localization key here instead text.