About

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

1. General information

1.1. Artifact

<dependencies>
    <dependency>
        <groupId>com.foreach.across.modules</groupId>
        <artifactId>bootstrap-ui-module</artifactId>
        <version>1.2.0.RELEASE</version>
    </dependency>
</dependencies>

1.2. Module dependencies

BootstrapUiModule only has a required dependency on AcrossWebModule.

2. What’s new in this version?

1.2.0.RELEASE

  • 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

1.1.0.RELEASE

  • 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

1.0.0.RELEASE

Initial public release available on Maven central.

3. ViewElement infrastructure

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

3.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}" />
Warning
You usually do not want to cache and reuse ViewElement components. It is better to cache ViewElementBuilder.

3.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.

Example

Assume we create a single ViewElement with a specific name.

@GetMapping("/render")
String get() {
  model.addAttribute( "myViewElement", new TextViewElement( "myViewElement", "some text" ) );
}

And we render it in the following snippet:

<h1>title</h1>
<div th:fragment="mycontent">
	<across:view element="${myViewElement}" />
</div>
  • /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

3.3. Default properties

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

name

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.

elementType

A required type identification for the element.

customTemplate

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.

3.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.

Examples:
  • 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)

Note
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.

3.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.

3.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.

3.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.

3.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
<!--[ax:title]-->
<input name="entity.title" id="entity.title" data-ax-dev-view-element="title" type="text" class="form-control" value="" required="required" />
<!--[/ax:title]-->
Note
It is not required for a ViewElement to have a name, nor is it required for that name to be unique.

4. Default elements

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

4.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

AlertViewElement

AlertViewElementBuilder

Create a Bootstrap alert component.

ButtonViewElement

ButtonViewElementBuilder

Create buttons or button links.

CheckboxFormElement

OptionFormElementBuilder

ColumnViewElement

ColumnViewElementBuilder

Creates a Bootstrap grid based layout.

DateTimeFormElement

DateTimeFormElementBuilder

FaIcon

Represents a Font Awesome icon.

FieldsetFormElement

FieldsetFormElementBuilder

FileUploadFormElement

FileUploadFormElementBuilder

Creates a file input element.

FormGroupElement

FormGroupElementBuilder

FormViewElement

FormViewElementBuilder

Create a form element with optional command attribute.

Glyphicon

Represents a Glyphicon icon.

HiddenFormElement

HiddenFormElementBuilder

InputGroupFormElement

InputGroupFormElementBuilder

LabelFormElement

LabelFormElementBuilder

LinkViewElement

LinkViewElementBuilder

Regular hyperlink.

NumericFormElement

NumericFormElementBuilder

RadioFormElement

OptionFormElementBuilder

Creates a single radio button.

SelectFormElement

OptionsFormElementBuilder

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).

SelectFormElement.Option

OptionFormElementBuilder

Creates a single select option.

StaticFormElement

Creates a readonly form-control.

TableViewElement

TableViewElementBuilder

TextareaFormElement

TextboxFormElementBuilder

Multi-line text field.

TextboxFormElement

TextboxFormElementBuilder

Single-line text field - supporting HTML5 types.

4.2. TableViewElementBuilder

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

4.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.

4.4. OptionsFormElementBuilder

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

4.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

selectAllText

BootstrapUiModule.SelectFormElementConfiguration.selectAllText

Select all

noneSelectedText

BootstrapUiModule.SelectFormElementConfiguration.noneSelectedText

Nothing selected

maxOptionsText

BootstrapUiModule.SelectFormElementConfiguration.maxOptionsText

Limit reached ({0} items max)

countSelectedText

BootstrapUiModule.SelectFormElementConfiguration.countSelectedText

{0} items selected

deselectAllText

BootstrapUiModule.SelectFormElementConfiguration.countSelectedText

Deselect all

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

4.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.

4.6. NumericFormElementBuilder

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

See NumericFormElementConfiguration for configuration options.

4.7. DateTimeFormElementBuilder

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

See DateTimeFormElementConfiguration for configuration options.

5. 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

DefaultNavComponentBuilder

bootstrapUiComponentFactory.nav()

Converts a Menu component to a Bootstrap nav.

PanelsNavComponentBuilder

bootstrapUiComponentFactory.panels()

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

BreadcrumbNavComponentBuilder

bootstrapUiComponentFactory.breadcrumb()

Generates a breadcrumb for the selected items of a Menu.

5.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", "#" );

model.addAttribute(
    "customNav",
    bootstrapUiComponentFactory.nav( menu ).tabs().build( builderContext )
);
Thymeleaf template
<nav>
    <across:view element="${customNav}" />
</nav>
HTML output generated
<nav>
    <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>
            </a>
            <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>
            </ul>
        </li>
    </ul>
</nav>

5.1.2. Supported nav styles

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

Method CSS appended Remarks

simple()

Default mode.

navbar()

navbar-nav

tabs()

nav-tabs

pills()

nav-pills

stacked()

nav-pills nav-stacked

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

5.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

5.1.4. Menu item title

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

5.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.

5.1.6. Customizing nav rendering through the Menu

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

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

The following attributes are support on Menu items:

nav:icon

ViewElement or ViewElementBuilder to be prepended to the item text.

nav:iconOnly

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

nav:linkViewElement

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.

nav:itemViewElement

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.

nav:keepAsGroup

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.

nav:keepGroupItem

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.

nav:panelStyle

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.

nav:renderAsPanel

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.

html:*

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.

5.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 )
    .and()
    .item( "/options/dl", "Download", "#" )
    .attribute( NavComponentBuilder.ATTR_ICON, new GlyphIcon( GlyphIcon.DOWNLOAD ) );

6. Creating custom view elements

6.1. Testing facilities

Across test contains some base classes for testing ViewElement infrastructure.

AbstractViewElementBuilderTest

A base unit test for any ViewElementBuilder that extends ViewElementBuilderSupport.

AbstractViewElementTemplateTest

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

6.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.

6.2.1. Web resource packages

The following packages are available for adding to your template:

Package Package name Description

JQueryWebResources

jquery

Registers JQuery library.

BootstrapUiWebResources

bootstrap

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

BootstrapUiFormElementsWebResources

bootstrapui-formelements

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

6.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.

Note
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
    } );
} );