Excerpt |
---|
|
Using SelectModel, SelectModelFactory and ValueEncoder for Select menus populated from a database |
Using Select With a List
The documentation for the Select Component and the Tapestry Tutorial provide simplistic examples of populating a drop-down menu (as the (X)HTML Select element) using comma-delimited strings and enums. However, most real-world Tapestry applications need to populate such menus using values from a database, commonly in the form of java.util.List objects. Doing so generally requires a SelectModel and a ValueEncoder bound to the Select component with its "model" and "encoder" parameters:
Code Block |
---|
<t:select t:id="colorMenu" value="selectedColor" model="ColorSelectModel" encoder="colorEncoder" />
|
In the above example, ColorSelectModel must be of type SelectModel, or anything that Tapestry knows how to coerce into a SelectModel, such as a List or a Map or a "value=label,value=label,..." delimited string, or anything Tapestry knows how to coerce into a List or Map, such as an Array or a comma-delimited String.
SelectModel
Wiki Markup |
---|
{float:right|background=#eee|padding=0 1em}
*JumpStart Demos:*
[Total Control Object Select|https://tapestry-jumpstart.org/jumpstart/examples/select/totalcontrolobject]
[ID Select|https://tapestry-jumpstart.org/jumpstart/examples/select/id]
[Easy ID Select|https://tapestry-jumpstart.org/jumpstart/examples/select/easyid]
{float} |
A SelectModel is a collection of options (specifically OptionModel objects) for a drop-down menu. Basically, each option is a value (an object) and a label (presented to the user).
...
Code Block |
---|
title | SelectWithListDemo.java (a page class) |
---|
|
@Property
private SelectModel colorSelectModel;
@Inject
SelectModelFactory selectModelFactory;
...
void setupRender() {
// invoke my service to find all colors, e.g. in the database
List<Color> colors = colorService.findAll();
// create a SelectModel from my list of colors
colorSelectModel = selectModelFactory.create(colors, "name");
}
|
...
Code Block |
---|
title | Color.java (partial) |
---|
|
...
@Override
public String toString() {
return String.valueOf(this.getId());
}
|
...
Wiki Markup |
---|
{float:right|background=#eee|padding=0 1em}
*JumpStart Demo:*
[Easy Object Select|httphttps://tapestry-jumpstart.doublenegative.com.auorg/jumpstart/examples/select/easyobject]
{float} |
...
Code Block |
---|
title | ColorEncoder.java (perhaps in your com.example.myappname.encoders package) |
---|
|
public class ColorEncoder implements ValueEncoder<Color>, ValueEncoderFactory<Color> {
@Inject
private ColorService colorService;
@Override
public String toClient(Color value) {
// return the given object's ID
return String.valueOf(value.getId());
}
@Override
public Color toValue(String id) {
// find the color object of the given ID in the database
return colorService.findById(Long.parseLong(id));
}
// let this ValueEncoder also serve as a ValueEncoderFactory
@Override
public ValueEncoder<Color> create(Class<Color> type) {
return this;
}
}
|
...
Code Block |
---|
title | SelectWithListDemo.java (a page class, partial) |
---|
|
. . .
public ValueEncoder<Color> getColorEncoder() {
return new ValueEncoder<Color>() {
@Override
public String toClient(Color value) {
// return the given object's ID
return String.valueOf(value.getId());
}
@Override
public Color toValue(String id) {
// find the color object of the given ID in the database
return colorService.findById(Long.parseLong(id));
}
};
}
|
...
Code Block |
---|
title | AppModule.java (partial) |
---|
|
...
public static void contributeValueEncoderSource(MappedConfiguration<Class<Color>,
ValueEncoderFactory<Color>> configuration) {
configuration.addInstance(Color.class, ColorEncoder.class);
}
|
...
Code Block |
---|
title | AppModule.java (partial) |
---|
|
...
public static void contributeValueEncoderSource(MappedConfiguration<Class,
ValueEncoderFactory> configuration)
{
configuration.addInstance(Color.class, ColorEncoder.class);
configuration.addInstance(SomeOtherType.class, SomeOtherTypeEncoder.class);
}
|
...
The Select component's "encoder" parameter is optional, but if the "value" parameter is bound to a complex object (not a simple String, Integer, etc.) and you don't provide a ValueEncoder with the "encoder" parameter (and one isn't provided automatically by, for example, the Tapestry Hibernate integration), you'll receive a "Could not find a coercion" exception (when you submit the form) as Tapestry tries to convert the selected option's encoded value back to the object in your Select's "value" parameter. To fix this, you'll either have to 1) provide a ValueEncoder, 2) provide a Coercion, or 3) use a simple value (String, Integer, etc.) for your Select's "value" parameter, and then you'll have to add logic in the corresponding onSuccess event listener method:
Code Block |
---|
title | SelectWithListDemo.tml (partial) |
---|
|
<t:select t:id="colorMenu" value="selectedColorId" model="ColorSelectModel" />
|
...
Code Block |
---|
title | SelectWithListDemo.java (partial) |
---|
|
...
public void onSuccessFromMyForm() {
// look up the color object from the ID selected
selectedColor = colorService.findById(selectedColorId);
...
}
|
...