1. Introduction

This document describes the form builder module. This module is used by software developers in specialized projects that need to generate edoras forms dynamically in Java. It provides Java builder APIs for individual form widgets and the form itself. These builders are used to generate an object representation of the form, which can then be converted directly to the corresponding JSON description. This JSON is then used by the edoras forms engine running in the browser.

1.1. Prerequisites

This document assumes a familiarity with edoras form concepts such as the form layout mechanism and the functionality supported by the available form widgets. This information can be found in the {modelerGuide}.

2. Obtaining the form builder

To include the form builder into your project you just need to add a dependency on the form builder JAR file, for example as a dependency in your Maven pom.xml:

Form builder dependency in Maven pom.xml
  <dependency>
          <groupId>com.edorasware.one</groupId>
          <artifactId>edoras-forms-builder</artifactId>
          <version>1.5.0.S83</version>
  </dependency>

3. Using the form builder

3.1. Overview

The following tutorial introduces the fundamental form builder concepts, and describes how a form description can be assembled and used to generate the corresponding JSON. Detailed documentation for the form builder API is provided in the form builder javadoc which can be obtained from edorasware support. Details of the functionality supported by specific form widget types and usage examples can be found in the edoras one Modeler Guide.

3.2. Widget builders

Before we can create a form we need to be able to create individual form widgets.

The form builder classes are located in the package com.edorasware.forms.builder, and builders for individual form widgets are located in the com.edorasware.forms.builder.component sub-package.

3.3. Creating a simple form widget

All widgets in the form builder are created using a widget-specific builder API. Each builder supports only the settings that are actually supported by the widget, so it should be impossible to create an invalid widget definition.

A new widget builder is created by calling the type-specific static builder() method. This method may require a number of parameters corresponding to the mandatory widget attributes and returns a builder object, providing additional methods to set the optional attributes. When the required settings have been made the build() method can be called to create the widget instance.

As an example, a text widget can be created as follows:

Creating a text widget
  // Create the component builder
  Text.Builder builder = Text.builder("id123", "{{textValue}}");
  
  // Adjust the optional settings
  builder.isRequired(true);
  builder.size(3);
  
  // Instantiate the component from the builder
  Text component = builder.build();

The additional configuration methods also return the builder instance, resulting in a fluent builder API. The configuration method calls can therefore be chained together and combined into a single statement:

Creating a text widget in a single statement
  Text component =
          Text.builder("id123", "{{textValue}}")
                  .isRequired(true)
                  .size(3)
                  .build();

This pattern is followed by all widget builders, with the appropriate variations in the supported configuration parameters.

3.4. Creating an empty form

The first step when creating a form is to create the form builder itself. A form builders is instantiated using the static factory method EdorasForm.builder(). When the builder has been created, the required form widgets can be added as required and the assembled form is then created by calling the build() method on the form builder. The following example shows how to create an empty form:

Creating an empty form with the form builder
  EdorasForm.Builder builder = EdorasForm.builder();
  EdorasForm form = builder.build();

3.5. Adding form widgets

As described in the edoras one Modeler Guide, edoras one form widgets are laid out in rows where each row is made up of 12 slots and a widget can span multiple slots within the row. Vertical spans across rows are not supported.

The form builder creates rows as required, with the first widget being added to the first free slot in the current row (building from left to right). The following example shows the construction of a form with a single row containing three widgets:

Adding widgets to the form
  EdorasForm.Builder builder = EdorasForm.builder();
  builder.add(Text.builder(
          "t1", "{{value1}}").label("Test 1", Alignment.TOP).size(2).build());
  builder.add(Text.builder(
          "t2", "{{value2}}").label("Test 2", Alignment.TOP).size(3).build());
  builder.add(Text.builder(
          "t3", "{{value3}}").label("Test 3", Alignment.TOP).size(4).build());
  EdorasForm form = builder.build();
example1

If there are not enough free slots to add the new widget in the current row then a new row will be created automatically. In the following example the final widget is too big to fit in the remaining 3 slots, so a new row is created:

Automatic creation of a new row
  EdorasForm.Builder builder = EdorasForm.builder();
  builder.add(Text.builder(
          "t1", "{{value1}}").label("Test 1", Alignment.TOP).size(3).build());
  builder.add(Text.builder(
          "t2", "{{value2}}").label("Test 2", Alignment.TOP).size(6).build());
  builder.add(Text.builder(
          "t3", "{{value3}}").label("Test 3", Alignment.TOP).size(9).build());
  EdorasForm form = builder.build();
example2

It is also possible to explicitly force the creation of a new row, which will then receive any further widgets that are added:

Explicit creation of a new row
  EdorasForm.Builder builder = EdorasForm.builder();
  builder.add(Text.builder(
          "t1", "{{value1}}").label("Test 1", Alignment.TOP).size(3).build());
  builder.nextRow();
  builder.add(Text.builder(
          "t2", "{{value2}}").label("Test 2", Alignment.TOP).size(6).build());
  builder.add(Text.builder(
          "t3", "{{value3}}").label("Test 3", Alignment.TOP).size(9).build());
  EdorasForm form = builder.build();
example3

To alter the horizontal placement of a widget within a row, a spacer widget of the appropriate size can simply be inserted beforehand (either at the start of a row or within it):

Using spacer widgets
          EdorasForm.Builder builder = EdorasForm.builder();
          builder.add(Spacer.builder(3).build());
          builder.add(Text.builder(
                  "t1", "{{value1}}").label("Test 1", Alignment.TOP).size(3).build());
          builder.nextRow();
          builder.add(Text.builder(
                  "t2", "{{value2}}").label("Test 2", Alignment.TOP).size(3).build());
          builder.add(Spacer.builder(3).build());
          builder.add(Text.builder(
                  "t3", "{{value3}}").label("Test 3", Alignment.TOP).size(3).build());
          EdorasForm form = builder.build();
  
          String json = FormBuilderUtil.toJson(form);
          with(json).assertThat("$.rows", hasSize(2))
                  .assertThat("$.rows[0].cols", hasSize(2))
                  .assertThat("$.rows[1].cols", hasSize(3))
                  .assertEquals("$.rows[0].cols[1].label", "Test 1")
                  .assertEquals("$.rows[1].cols[0].label", "Test 2")
                  .assertEquals("$.rows[1].cols[2].label", "Test 3");
      }
  
      @Test
      public void addCollection() throws JsonProcessingException {
          List<EdorasFormComponent> components = newLinkedList();
          components.add(Text.builder("t1", "{{value1}}").size(2).build());
          components.add(Text.builder("t2", "{{value2}}").size(3).build());
          components.add(Text.builder("t3", "{{value3}}").size(4).build());
  
          EdorasForm.Builder builder = EdorasForm.builder();
          builder.addAll(components);
          EdorasForm form = builder.build();
  
          String json = FormBuilderUtil.toJson(form);
          with(json).assertThat("$.rows", hasSize(1)).assertThat("$.rows[0].cols", hasSize(3));
      }
  
      @Test
      public void addVarargs() throws JsonProcessingException {
          Text t1 = Text.builder("t1", "{{value1}}").size(2).build();
          Text t2 = Text.builder("t2", "{{value2}}").size(3).build();
          Text t3 = Text.builder("t3", "{{value3}}").size(4).build();
  
          EdorasForm.Builder builder = EdorasForm.builder();
          builder.addAll(t1, t2, t3);
          EdorasForm form = builder.build();
  
          String json = FormBuilderUtil.toJson(form);
          with(json).assertThat("$.rows", hasSize(1)).assertThat("$.rows[0].cols", hasSize(3));
      }
  }
example4

As well as adding individual widgets to the form, it is also possible to add multiple widgets with a single call, either as a collection:

Adding multiple widgets as a collection
  List<EdorasFormComponent> components = newLinkedList();
  components.add(Text.builder("t1", "{{value1}}").size(2).build());
  components.add(Text.builder("t2", "{{value2}}").size(3).build());
  components.add(Text.builder("t3", "{{value3}}").size(4).build());
  
  EdorasForm.Builder builder = EdorasForm.builder();
  builder.addAll(components);
  EdorasForm form = builder.build();

or using a varargs list:

Adding multiple widgets with varargs
  Text t1 = Text.builder("t1", "{{value1}}").size(2).build();
  Text t2 = Text.builder("t2", "{{value2}}").size(3).build();
  Text t3 = Text.builder("t3", "{{value3}}").size(4).build();
  
  EdorasForm.Builder builder = EdorasForm.builder();
  builder.addAll(t1, t2, t3);
  EdorasForm form = builder.build();

3.6. Creating the form JSON

When an EdorasForm definition has been created using the form builder, it can be converted into the corresponding JSON representation using the static FormBuilderUtil.toJson() method:

Creating the form JSON
  String json = FormBuilderUtil.toJson(form);