DomUI

Forms

A form is not a component in DomUI. There is no Form class to add to a page and no form node to put controls in: a form is what the FormBuilder leaves behind after it has been handed a panel and a series of controls.

ContentPanel cp = new ContentPanel();
add(cp);

Text2<String> title = new Text2<>(String.class);
Text2<Integer> copies = new Text2<>(Integer.class);
DateInput2 released = new DateInput2();

FormBuilder fb = new FormBuilder(cp);
fb.label("Album title").mandatory().control(title);
fb.label("Copies in stock").control(copies);
fb.label("Released").control(released);

The three things it does

The builder takes the controls of the component groups around it and

  • puts a label in front of each one and lines the pairs up,
  • creates the control for you when you name a property instead of a control, and binds it to that property,
  • carries the things a whole form shares - read only, disabled, the width of the labels - so that they are not repeated on every row.
Page What it covers
Labels and controls the pair the form is made of: label(), control(), mandatory(), hints, and rows that are not controls at all.
A form from properties property(): the control made from metadata and bound to the property, and the readOnly/disabled bindings around it.
Laying the form out vertical and horizontal, where a form ends, more than one control in a pair, and the css of a row.

The shape of the call

Every row is one chain, and the chain always ends in control() or item():

fb.label("Album title").mandatory().hint("As it is on the sleeve").control(title);
fb.property(album, Album_.title()).readOnly().control();

Between the two ends sit the things this row is to be told - the label, whether it is mandatory, whether it is read only, what css it carries. The builder holds that row while it is being described, so the chain must be finished before the next one starts; a label() that is never followed by a control() makes the next row fail with You need to end the builder pattern with a call to 'control()'.

control() with no argument only exists on a property() chain, where the metadata says what to make. Everywhere else the control is made first and handed over.

What ends up on the page

The builder does not put the controls into the panel directly. It builds a div per form, a div per pair, and a div for the label and the control of that pair:

<div class="ui-f5 ui-f5-v">
    <div class="ui-f5-pair ui-f5-pair-v">
        <div class="ui-f5-lbl ui-f5-lbl-v"><label for="...">Album title</label></div>
        <div class="ui-f5-ctl ui-f5-ctl-v">...the control...</div>
    </div>
    ...
</div>

Those divs are flexbox: the pair is a row in a vertical form and an inline block in a horizontal one, and the label div has a minimum width, which is what makes the controls line up under each other. Nothing about it is a table.

The class names say ui-f5 while the package says form4. The builder is component2.form4.FormBuilder; ui-f5 is the fifth generation of the css it emits. The two numbers are not related to each other.

Last modified: 2026/09/06