1. About

The BootstrapUiModule provides infrastructure for generating user interfaces based on Bootstrap.

2. General information

2.1. Artifact


2.2. Module dependencies

BootstrapUiModule only has a required dependency on AcrossWebModule.

3. What’s new in this version?


  • DateTimeFormElements now have support for LocalDate, LocalTime, LocalDateTime

  • FormViewElement and FormViewElementBuilder now allow you to configure either a command object or an Errors instance directly

    • this makes it easier to resolve field errors for the FormGroupElement members of the form, without having to know the model attribute name

    • when using the errors property directly, it is no longer required that the command object binding result is present on the model

  • the default builders now usually support localized text patterns for any text property

    • for example: BootstrapUiBuilders.button().title("#{my.button=Default text}")

  • TextboxFormElement rendered inside a FormGroupElement which detects field errors, will now render the original input value if there are errors present

  • direct use of BootstrapUiFactory or BootstrapUiComponentFactory is now discouraged, these interfaces have been deprecated

    • use the stateless BootstrapUiBuilders facade instead

  • added a TooltipViewElement with corresponding builder to quickly generate a tooltip with a default question mark icon

    • see BootstrapUiBuilders.tooltip() for a builder method

  • FormGroupElement has been refactored to support more descriptions

    • a tooltip can be set which will be added to the label - after the label text and required indicator

    • a descriptionBlock can be set which will be added to the group before the control

    • a helpBlock can be set which will be added to the group after the control

    • the property renderHelpBlockBeforeControl has been removed as the descriptionBlock is always rendered before the control, and the helpBlock always after the control


  • added BootstrapUiBuilders class that is a static facade for all builders

    • BootstrapUiFactory and BootstrapUiComponentFactory beans are still available, but because both of them operate stateless, the facade was added to reduce wiring verbosity

  • textbox improvements:

    • TextboxFormElement has a property disableLineBreaks that will disable the ENTER key

    • the TextboxFormElementBuilder will generate a valid single-line auto-sizing textarea if auto-size is requested on a normal textbox


  • added several builders for rendering menu structures: DefaultNavComponentBuilder, BreadcrumbNavComponentBuilder and PanelsNavComponentBuilder

    • all nav related builders now also support message code snippets in Menu titles: putting for example #{my.code=Item title} as value will be replaced by the Locale specific message for my.code or by Item title if the message code can’t be resolved

  • added support for bootstrap-select dropdown lists on OptionsFormElementBuilder and SelectFormElement

    • allows for more advanced and user-friendly dropdown controls

  • the BootstrapUiModule javascript library supports adding initializer extensions


Initial public release available on Maven central.

4. ViewElement infrastructure

BootstrapUiModule provides a whole set of ViewElement infrastructure, meant for programmatically building user interfaces.

4.1. ViewElement

ViewElement implementations usually correspond to one or more HTML nodes. BootstrapUiModule comes with a default set ViewElement implementation for rendering Bootstrap markup elements. This is done through a collection of Thymeleaf processors for rendering ViewElement components.

You can render a ViewElement available on the model anywhere in a Thymeleaf template by using the across:view tag.

<across:view element="${myViewElement}" />
You usually do not want to cache and reuse ViewElement components. It is better to cache ViewElementBuilder.

4.2. Partial rendering

It is possible to render only a single ViewElement by passing the element name. In that case all code will still be executed and all markup built, but only the markup of those elements will be written to the response.

The ViewElement name is specified in the partial parameter with a :: prefix.


Assume we create a single ViewElement with a specific name.

String get() {
  model.addAttribute( "myViewElement", new TextViewElement( "myViewElement", "some text" ) );

And we render it in the following snippet:

<div th:fragment="mycontent">
        <across:view element="${myViewElement}" />
  • /render would output <h1>title</h1><div>some text</div>

  • /render?_partial=mycontent would output <div>some text</div>

  • /render?_partial=::myViewElement would output some text

  • /render?_partial=mycontent::myViewElement would also output some text

4.3. Default properties

In its most simple form, a ViewElement has the following properties:


An optional internal name of the element. This name can be used to retrieve the element from a ContainerViewElement. Use ContainerViewElementUtils to query and modify containers.

See development mode rendering for more information to retrieve generated view names.


A required type identification for the element.


An optional template name. If a custom template is specified, it will be used to render the ViewElement instead of the default processor. By default only Thymeleaf templates are supported.

4.3.1. Custom template

Every ViewElement allows you to configure a customTemplate. Only Thymeleaf fragments are supported, if you specify a Thymeleaf template without a fragment, a render(component) fragment will be appended. The component variable will always contain the ViewElement instance that is being rendered.

You can use a different input variable by specifying the ${component} manually in your template specification.

  • th/mymodule/mytemplate results in th/mymodule/mytemplate :: render(component)

  • th/mymodule/mytemplate :: myfragment results in th/mymodule/mytemplate :: myfragment(component)

  • th/mymodule/mytemplate :: myfragment(${someModelAttribute},${component}) results in th/mymodule/mytemplate :: myfragment(attributeValue,component)

You should only use model attributes that are sure to be available when the template is being rendered. It is usually best to pass the required values as attributes on the ViewElement itself.

You can use the TemplateViewElement if you only want to render a custom template and optionally pass it some attributes.

4.4. ViewElementBuilder

A ViewElementBuilder is a simple API for creating a ViewElement instance based on a configuration and a given ViewElementBuilderContext.

The ViewElementBuilderContext represents the runtime context when creating the element. It is a way to pass attributes required for building the elements, and it also gives access to default request related beans like the WebResourceRegistry or the WebAppLinkBuilder.

BootstrapUiModule comes with a number of default ViewElementBuilder implementations for both simple elements and more complex components.

4.4.1. Global ViewElementBuilderContext

Most ViewElementBuilder implementations extend GlobalContextSupportingViewElementBuilder. This class provides a parameterless build() method that will attempt to retrieve a ViewElementBuilderContext from the current thread, or from the request attached to the thread. If no global ViewElementBuilderContext is registered however, calls to build() will throw an exception.

See the ViewElementBuilderContextInterceptor for an interceptor that creates a global ViewElementBuilderContext.

4.4.2. ViewElementBuilderContext in controllers

If there is a global ViewElementBuilderContext available, you can also ViewElementBuilderContext as a method argument in web controller methods.

The ViewElementBuilderContext provides a buildLink(String) method that will resolve a link using the WebAppLinkBuilder attribute that is available on the builder context. By default the request-bound WebAppLinkBuilder is already set.

4.5. Development mode rendering

If development mode is active, all ViewElement names will be rendered in the markup. Start and end of the element rendering will be marked by a HTML comment. If the ViewElement is a node (xml-type element) it will also have a data attribute data-ax-dev-view-element containing the name.

Example markup when rendered in development mode
<input name="entity.title" id="entity.title" data-ax-dev-view-element="title" type="text" class="form-control" value="" required="required" />
It is not required for a ViewElement to have a name, nor is it required for that name to be unique.

5. Default elements

Most default elements can be created through the BootstrapUiFactory (instance) of BootstrapUiBuilders (static facade to the factories).

5.1. Overview

Although elements can be created directly, most have an equivalent ViewElementBuilder. The builder implementation is rarely created directly but through the BootstrapUiFactory.

BootstrapUiElements contains the list of constants that define the specific element types.

Element Builder Description



Create a Bootstrap alert component.



Configure and create a Typeahead based auto-suggest textbox. See also the AutoSuggestFormElementConfiguration for configuration of the required datasets.



Create buttons or button links.





Creates a Bootstrap grid based layout.




Represents a Font Awesome icon.





Creates a file input element.





Create a form element with optional command attribute.


Represents a Glyphicon icon.









Regular hyperlink.





Creates a single radio button.



Creates a select control, either a regular HTML dropdown or a bootstrap-select. Which type gets created depends on the presence of a SelectFormElementConfiguration object (configuration property).



Creates a single select option.


Creates a readonly form-control.





Multi-line text field.



Single-line text field - supporting HTML5 types.



Generate a simple tooltip element (text shown when hovering). By default this is a link with a question mark icon.

5.2. TableViewElementBuilder

Generate Bootstrap markup table structures. Holds nested builders for head, foot and body sections.

5.3. TextboxFormElementBuilder

Will add textbox or textarea, based on multiline or not. Also supports typing a textbox element. In case of textarea will by default enable autosizing of the textarea and will register the javascript to do so.

5.4. OptionsFormElementBuilder

To quickly create a list of options, either as a select, list of checkboxes or list or radio buttons.

5.4.1. bootstrap-select support

If you want to create a more advanced bootstrap-select dropdown instead of a simple HTML select, you can do so by specifying a SelectFormElementConfiguration object. See the respective javadoc for all configuration properties.

Message codes

The SelectFormElementConfiguration allows you to configure the default text for the control. These properties support message code text snippets which will be replaced if a SelectFormElement is built using the OptionsFormElementBuilder.

The following default message codes are used:

Property Message code Default text



Select all



Nothing selected



Limit reached ({0} items max)



{0} items selected



Deselect all

Message code replacement is performed when SelectFormElementConfiguration.localize() is called. This is done automatically when using an OptionsFormElementBuilder

5.5. FormGroupElementBuilder

Takes a label and a control. Can optionally take some help text. Will render as a form group and will attempt to link the label to the control.

5.6. NumericFormElementBuilder

Uses the JQuery autoNumeric plugin. Supports decimal precision, localization and adding symbols (eg. for currency).

See NumericFormElementConfiguration for configuration options.

5.7. DateTimeFormElementBuilder

Represented as a date/time picker. Uses the Eonasdan datetimepicker JQuery plugin.

See DateTimeFormElementConfiguration for configuration options.

6. Custom component builders

Accessible through the BootstrapUiComponentFactory.

The BootstrapUiComponentFactory provides several builders for generating markup based on an Across Menu. All builders extend NavComponentBuilder and support some of the same options.

Builder class Factory method Description



Converts a Menu component to a Bootstrap nav.



Converts a Menu component to a HTML5 nav where every group of Menu items is rendered as a panel with a heading.



Generates a breadcrumb for the selected items of a Menu.

6.1.1. Example

An example of generating a nav structure from a Menu.

Custom menu definition and rendering
PathBasedMenuBuilder menu = new PathBasedMenuBuilder();
menu.item( "/one", "One", "#" ).order( 1 ).and()
    .group( "/two", "Two" ).order( 2 ).and()
    .item( "/two/one", "Sub item 1", "#" ).and()
    .item( "/two/two", "Sub item 2", "#" );

    bootstrapUiComponentFactory.nav( menu ).tabs().build( builderContext )
Thymeleaf template
    <across:view element="${customNav}" />
HTML output generated
    <ul class="nav nav-tabs">
        <li><a href="#" title="One">One</a></li>
        <li class="dropdown">
            <a data-toggle="dropdown" href="#" title="Two" class="dropdown-toggle">
                Two <span class="caret"></span>
            <ul class="dropdown-menu">
                <li><a href="#" title="Sub item 1">Sub item 1</a></li>
                <li><a href="#" title="Sub item 2">Sub item 2</a></li>

6.1.2. Supported nav styles

You can generate specific nav structures using the DefaultNavComponentBuilder by specifying a nav style.

Method CSS appended Remarks


Default mode.








nav-pills nav-stacked

All NavComponentBuilder implementations also support custom HTML attributes to be configured directly on the root element.

6.1.3. Default menu conversion behaviour

When mapping a Menu onto a nav, the following rules are followed:

  • only 3 levels of items/groups are supported in the Menu

  • an item is always rendered as a single item, even if it has children

  • an item or group are only rendered if they are not disabled

  • when an item is selected, the item itself as well as all its parent will have the active css class

  • a group is only rendered if it has at least one non-disabled child

  • a group is rendered as a dropdown

  • a group inside a group is rendered as a labeled section in the dropdown

  • a group having only a single item is rendered as that single item unless the attribute nav:keepAsGroup is set

All nav related builders support message code snippets in Menu titles.

Putting for example #{my.code=Item title} as title property will be replaced by the Locale specific message for my.code or by Item title if that message code can’t be resolved

6.1.5. Replacing group label by the selected item

By default the label of a dropdown will always be the title of the group. If you want the label to be replaced by the label of the selected item, you should configure the NavComponentBuilder with replaceGroupBySelectedItem.

With replaceGroupBySelectedItem true, if no item is selected in the group, the dropdown label will still be the title of the group. If an item is selected however, the dropdown label will be the item label, unless the group itself has the attribute nav:keepGroupItem set to true.

6.1.6. Customizing nav rendering through the Menu

You can influence the generated output by setting reserved attributes on the Menu items.

Attribute names mentioned here are available as constants on the NavComponentBuilder class.

The following attributes are support on Menu items:


ViewElement or ViewElementBuilder to be prepended to the item text.


Only applicable on a group. If an icon is set, this will render the group itself as only the icon.


ViewElement or ViewElementBuilder to use when rendering the link inside the list item. This will replace the standard link with the element generated. Note that any value for nav:icon will be ignored.

Possible child items will still be rendered as a nested unordered list if the item is a group. A custom link should handle opening the dropdown in that case.


ViewElement or ViewElementBuilder to use for rendering the entire list item of that Menu. The ViewElement should take care of the full rendering, including any possible children.


Only applicable on a group. If set to true the group will always be rendered as a group, even though there is only a single item in it.


Only applicable on a group and if replaceGroupBySelectedItem is set to true. If so and nav:keepGroupItem is set to true, the replace action will be suppressed and the group label will always be rendered.

NOTE: Attribute is only relevant for a DefaultNavComponentBuilder.


Holds the CSS class that determines the panel styling. If set, the default panel-default class will be omitted. Only applicable on group menu items that would result in a panel being rendered.

NOTE: Attribute is only relevant for a PanelsNavComponentBuilder.


If set to false on an group menu item that would be rendered as a panel (a group on the top level), no panel will be rendered but a sidebar nav list will directly be rendered. Optionally a title will still be included if the group has one. Non-panel lists do not support groups as items, these will be ignored.

NOTE: Attribute is only relevant for a PanelsNavComponentBuilder.


Any attribute with a name starting with html: will be added as html attribute to the list item. Name of the html attribute will be the menu attribute name without the html: prefix.

ViewElementBuilder attribute values

Some attributes support a ViewElementBuilder. When rendering using a ViewElementBuilder the ViewElementBuilderContext will have an attribute NavComponentBuilder.currentMenuItem that contains the Menu the builder is rendering.

6.1.7. Examples

Adding an icon

Adding an icon to an item or group is easily done by setting the nav:icon attribute with a ViewElement or ViewElementBuilder value.

Example adding an icon as attribute
menu.item( "/dl", "Download", "#" )
    .attribute( NavComponentBuilder.ATTR_ICON, new GlyphIcon( GlyphIcon.DOWNLOAD ) )
    .order( 1 );
An icon based dropdown

If you set attribute nav:iconOnly to true, the dropdown will only render the icon for the group. If there is no icon value set on the group item, the dropdown will render the group title.

The children of the group (dropdown options) will always be rendered as full items.

If however the dropdown label is replaced by an item or the group only contains a single item, the item will also be rendered as only an icon.

Example creating a dropdown represented by a single icon
menu.group( "/options", "Options", "#" )
    .attribute( NavComponentBuilder.ATTR_ICON, new GlyphIcon( GlyphIcon.COG ) )
    .attribute( NavComponentBuilder.ATTR_ICON_ONLY, true )
    .item( "/options/dl", "Download", "#" )
    .attribute( NavComponentBuilder.ATTR_ICON, new GlyphIcon( GlyphIcon.DOWNLOAD ) );

7. Creating custom view elements

7.1. Testing facilities

Across test contains some base classes for testing ViewElement infrastructure.


A base unit test for any ViewElementBuilder that extends ViewElementBuilderSupport.


Base integration test class for testing the rendering of a ViewElement. Provides useful methods for rendering and inspecting the generated output.

7.2. BootstrapUiModule web resources

To provide the components client-side behaviour, BootstrapUiModule uses several web resource packages. These often get registered automatically by the ViewElementBuilder used for generating a ViewElement. Dependent packages will automatically be added as well.

7.2.1. Web resource packages

The following packages are available for adding to your template:

Package Package name Description



Registers JQuery library.



Registers default Bootstrap CSS and javascript library.
Depends on JQueryWebResources.



Registers additional javascript and css for form element components like the datepicker, bootstrap-select etc.
Depends on BootstrapUiWebResources.

7.2.2. BootstrapUiModule javascript object

When the javascript is registered correctly, a single BootstrapUiModule global object is available. BootstrapUiModule javascript is fully JQuery based.

All BootstrapUiModule javascript can then be initialized by calling BootstrapUiModule.initializeFormElements(). This method optionally takes an argument that is the node in which the form elements should be initialized.

This is automatically done on document load, but when using AJAX fragment rendering, you usually want to re-initialize the DOM element that was updated.

Custom initializers

You can easily add a custom initializer function by adding it with BootstrapUiModule.registerInitializer( callback ). There is no need to manually execute your callback on document load, as that will happen automatically by the BootstrapUiModule.

Don’t execute your callback on document load and then add it to the initializers. Execution will happen automatically when calling registerInitializer().
Example registering a custom initializer
BootstrapUiModule.registerInitializer( function( node ) {
    $( '[data-my-attribute]', node ).each( function() {
        // initialize all elements with that attribute
    } );
} );


1. Customizing a datetime picker

Every DateTimeFormElement will be turned into a datetime picker as seen in the section DateTimeFormElementBuilder

Sometimes you want to attach a custom javascript event to this datetime picker.

In the following example we will attach an on change event, to refresh a portion of the screen with an AJAX call.

BootstrapUiModule.registerInitializer( function( node ) {
  $( '[data-bootstrapui-datetimepicker]', node ).first().datetimepicker().on( 'dp.change', function( event ) {
    $.get( Across.AdminWebModule.rootPath + "/getdata?_partial=::summary&selectedDate=" + event.date.format( "YYYY-MM-DD" ), function( data ) {
      $( '#form-right-column' ).replaceWith( data );
    } );
  } );
}, true );

The BootstrapUiModule.registerInitializer() ensures that your function is registered after that bootstrap-ui has loaded.

After this is done you can register on the dp.change to do the AJAX call. See http://eonasdan.github.io/bootstrap-datetimepicker/Events/ for more events supported by this datetime picker.

The AJAX call will render a partial and replace the content.

If your callback function is not executed inside BootstrapUiModule.registerInitializer(), then this might be due to the fact that your code is executing before that bootstrapui-formelements.js is loaded. Sometimes Across fails to include this package in time. If this is the case, you can add this WebResource by adding it to your Controller as shown in the example below.
        public void registerJavascript( WebResourceRegistry registry ) {
                registry.addPackage( BootstrapUiFormElementsWebResources.NAME );
                registry.addWithKey( WebResource.JAVASCRIPT_PAGE_END, "data-js", "/static/omega/js/data.js", WebResource.VIEWS );
If you encounter an undefined error when calling datetimepicker(), this might be due to including jQuery twice in your DOM.