Prefixing form control names

In this guide you’ll learn:

  • how to safely add a custom name prefix to all form controls in a container

  • what a ControlNamePrefixAdjuster is

Summary

If you want to safely apply a name prefix to all controls in a container, you should use a ControlNamePrefixAdjuster or one of the utility functions:

BootstrapElementUtils.prefixControlNames(...)
BootstrapElementUtils.replaceControlNamesPrefix(...)

A simple example

Suppose you have a simple Todo class, representing a todo item with a description and a person responsible (the owner).

@Data
class Todo
{
    private String owner;
    private String description;
}

And you have a method that creates the form groups for a single item:

ContainerViewElementBuilder createTodoForm( Todo item ) {
    return container()
            .add(
                    formGroup(
                            label( "Owner" ),
                            textbox().controlName( "owner" ).text( item.getOwner() )
                    )
            )
            .add(
                    formGroup(
                            label( "Description" ),
                            textbox().controlName( "description" ).text( item.getDescription() )
                    )
            );
}

The generated markup for the owner form group would then be:

<div class="form-group">
    <label for="owner" class="control-label">Owner</label>
    <input name="owner" id="owner" type="text" class="form-control">
</div>

Suppose now you have a list of todo items and you wish to render a single form with all your todo items. Every item should have its own text boxes with a unique name.

Instead of generating a unique name when building the form groups, we can post-process each form group container using a utilities method.

List<Todo> todos;
FormViewElementBuilder form = BootstrapUiBuilders.form();

for ( int i = 0; i < todos.size(); i++ ) {
    Todo todo = todos.get(i);
    form.add(
        createTodoForm( todo )
            .postProcessor( BootstrapElementUtils.prefixControlNames( "todoList[" + i + "]" ) )
    );
}

We post-process the container and add a unique name prefix to all FormInputControl members. The prefixControlNames() method will recurse through all members of the container and ensure they are processed if necessary.

The resulting markup for the first form-group would then be:

<div class="form-group">
    <label for="todoList[0].owner" class="control-label">Owner</label>
    <input name="todoList[0].owner" id="todoList[0].owner" type="text" class="form-control">
</div>

Note that we specified a prefix of todoList[0] to be added to owner, but the resulting name is todoList[0].owner, with an additional . (dot) having been inserted. This is because the default configuration assumes you want to apply a prefix for model binding, which means your control name usually represents a valid bean path. For brevity’s sake it is not required to specify the . suffix yourself in this case.

See ControlNamePrefixAdjuster if you want to change this behaviour.

Builder or element

In our example we return a ContainerViewElementBuilder, but we could have just as easily returned the resulting ContainerViewElement instance.

Applying control prefixing
// Using as a ViewElementPostProcessor
createTodoForm( todo )
    .postProcessor( BootstrapElementUtils.prefixControlNames( "todoList[" + i + "]" ) )
    .build();

// Using as a Consumer<ContainerViewElement>
ContainerViewElement form = createTodoForm( todo ).build();
form.apply( BootstrapElementUtils.prefixControlNames( "todoList[" + i + "]" ) );

Replacing an existing prefix

In addition to just prefixing control names, it is also possible to replace an existing prefix. Only controls that have a name starting with the existing prefix will be modified.

Removing a prefix from controls by replacing it with an empty string
container.apply( BootstrapElementUtils.replaceControlNamesPrefix( "nested.", "" ) );

ControlNamePrefixAdjuster

The utility methods prefixControlNames(String) and replaceControlNamesPrefix(String, String) return an instance of ControlNamePrefixAdjuster. This is a reusable component that will apply control name changes based on the settings configured.

The default settings are:

  • insert an additional . (dot) in the control name when necessary

  • consider a control name that starts with _ (underscore) as the hidden variant of another control, apply the prefix after the underscore

  • when applied to a container, recurse through the entire child tree and change control names of any matching element

All of these settings can be modified to change the behaviour of that instance.

Example custom control name replacing
BootstrapElementUtils.prefixControlNames( "yourbox" )
            .elementPredicate( e -> e instanceof CheckboxFormElement ) (1)
            .prefixToReplace( "mybox." ) (2)
            .ignoreUnderscore( false ) (3)
            .recurse( false ) (4)
            .insertDotSeparator( false ) (5)
            .accept( checkbox ); (6)
1 only modify controls that match the predicate: they must be a CheckboxFormElement
2 only change the name if it starts with mybox.
3 do not ignore the underscore at the beginning of a name, in this example a control name starting with _mybox would not be modified
4 do not modify any children of the control
5 don’t automatically add a ., in this case mybox.active would be modified to yourboxactive
6 perform the prefixing on the control passed in
You should only use a ControlNamePrefixAdjuster for changing control names of controls. Not only is it smarter with regards to the actual control name generation, it also ensures that only valid form controls are adjusted when applied to a container.