-
Notifications
You must be signed in to change notification settings - Fork 30
Composite Components
Composite Components are custom components that comprise a number of standard components to make a new component that you can invoke from your JSP page.
The simplest way to make a composite is to use a JSP tag file. As an example, let's build a composite formgroup
with a text control and a dropdown button, as shown in the Bootstrap documentation. We'll call this composite a "dropdowninput".
Firstly, we need to create a tag file in WEB-INF/tags/ called dropdowninput.tag (the path is irrelevant but the filename is not):
<%@ taglib uri="http://bootstrapjsp.org/" prefix="b" %>
<%@ attribute name="label" %>
<%@ attribute name="name" %>
<%@ attribute name="value" %>
<b:inputgroup>
<b:formcontrol name="${name}" value="${value}"/>
<b:inputgroupbutton>
<b:button mold="dropdown" label="${label}"/>
<b:dropdownmenu>
<jsp:doBody/>
</b:dropdownmenu>
</b:inputgroupbutton>
</b:inputgroup>
Now it can be referenced from a JSP page using a taglib directive (I've used an "ex" prefix for "example", or "extra"). Note how the menu items are added to the menu because of where the <jsp:doBody>
tag is in the tag file:
<%@ taglib uri="http://bootstrapjsp.org/" prefix="b" %>
<%@ taglib prefix="ex" tagdir="/WEB-INF/tags" %>
<ex:dropdowninput name="name" value="value" label="Click Me">
<b:menuitem>An Action</b:menuitem>
<b:menuitem>Another Action</b:menuitem>
</ex:dropdowninput>
The trouble with this though is that only the label
, name
and value
attributes are supported. What if I wanted, for example to add a class
attribute to my composite from my JSP page? I could of course add the attribute to the tag file, but then if I needed to add an id
attribute later, I'd be going back to add yet another attribute.
Fortunately, Bootstrap.jsp components allow you to set a number of attributes at once by passing a Map of name/value pairs in the attributes
attribute. Fortunate, because a Map is how dynamic attributes are handled by JSP tag files.
Amending the tag file to the following will allow me to add any attribute I want to my new composite:
<%@ tag dynamic-attributes="attributes" %>
<%@ taglib uri="http://bootstrapjsp.org/" prefix="b" %>
<%@ attribute name="label" %>
<%@ attribute name="name" %>
<%@ attribute name="value" %>
<b:inputgroup attributes="${attributes}">
<b:formcontrol name="${name}" value="${value}"/>
<b:inputgroupbutton>
<b:button mold="dropdown" label="${label}"/>
<b:dropdownmenu>
<jsp:doBody/>
</b:dropdownmenu>
</b:inputgroupbutton>
</b:inputgroup>
<%@ taglib uri="http://bootstrapjsp.org/" prefix="b" %>
<%@ taglib prefix="ex" tagdir="/WEB-INF/tags" %>
<ex:dropdowninput name="name" value="value" label="Click Me" class="dropdowninput" id="input1">
<b:menuitem>An Action</b:menuitem>
<b:menuitem>Another Action</b:menuitem>
</ex:dropdowninput>
All Components can be instantiated and appended to each other in Java, so you can build Composites in Java using the Molding System to append child Components.
The above Composite could be written as a Mold, with an extra supported attribute direction
that takes a value 'ltr' or 'rtl' to control which side of the input the button appears, like so:
import org.bootstrapjsp.facet.Mold;
import org.bootstrapjsp.support.NestedTagSupport;
import org.bootstrapjsp.tags.core.dropdown.DropdownMenu;
import org.bootstrapjsp.tags.core.form.FormControl;
import org.bootstrapjsp.tags.core.inputgroup.InputGroup;
import org.bootstrapjsp.tags.core.inputgroup.InputGroupButton;
import org.bootstrapjsp.tags.core.misc.Button;
public class DropdownInputMold extends Mold<InputGroup> {
private final Button button = new Button();
private final FormControl formControl = new FormControl();
private boolean leftToRight = true;
public DropdownInputMold() {
this.button.setAttribute("mold", "dropdown");
}
@Override
public void apply(InputGroup inputGroup, String mold) {
// Create a new InputGroupButton with a dropdown button and menu inside
final InputGroupButton inputGroupButton = new InputGroupButton();
inputGroupButton.appendChild(this.button, NestedTagSupport.BEFORE_BODY);
inputGroupButton.appendChild(new DropdownMenu());
// Add the form control to the InputGroup either first or last
int position = this.leftToRight ? NestedTagSupport.BEFORE_BODY : NestedTagSupport.AFTER_BODY;
inputGroup.appendChild(this.formControl, position);
// Add the InputGroupButton (this wraps around the body of the
// InputGroup tag so that menu items can be added to it in the JSP)
inputGroup.appendChild(inputGroupButton);
}
@Override
public boolean setAttribute(String name, Object value) {
if ("label".equals(name)) {
// The label attribute should be applied to the button
this.button.setAttribute(name, value);
return true;
} else if ("value".equals(name) || "type".equals(name)) {
// The value and type attributes should be applied to the form control
this.formControl.setAttribute(name, value);
return true;
} else if ("direction".equals(name)) {
if ("ltr".equals(value) || "rtl".equals(value)) {
this.leftToRight = "ltr".equals(value);
return true;
}
}
// Otherwise, handle the attribute normally
return false;
}
}
The important thing to notice is how the position parameter is used with the appendChild() method to control whether the child is appended before, after, or around the body of the InputGroup.
Our mold can be now be used like so:
<%@ taglib uri="http://bootstrapjsp.org/" prefix="b" %>
<b:inputgroup mold="DropdownInputMold" name="name" value="value" label="Click Me">
<b:menuitem>An Action</b:menuitem>
<b:menuitem>Another Action</b:menuitem>
</b:inputgoup>
Using a mold is more involved and less intuitive than a tag file, but for Composites with complex logic is often easier.
You can of course use a tag file to produce a new tag that uses a mold to build the Composite:
<%@ tag dynamic-attributes="attributes" %>
<%@ taglib uri="http://bootstrapjsp.org/" prefix="b" %>
<b:inputgroup mold="DropdownInputMold" attributes="${attributes}">
<jsp:doBody/>
</b:inputgroup>