Strecks currently comes with two data binding annotations:
@BindSimple
is used to bind the possibly converted value of a form property (usually a String
) to a domain model property.@BindSelect
is used to bind the selection for a form property, usually from a drop down list or radio button, to a domain model.These are described in a bit more detail below.
The usage of @BindSimple
is shown below from an ActionForm subclass implementation.
private HolidayBooking booking; private String days; @BindSimple(expression = "booking.days") public String getDays() { return days; } @ValidateInteger(key = "holidaybookingform.days.number") public void setDays(String days) { this.days = days; }
The booking object is the domain object which contains an Integer
property called days
.
The expression booking.days
is used to reference this property and hence perform the binding.
Under the covers, the Jakarta Commons BeanUtils library is used, as it is elsewhere in Struts.
Note that a type conversion needs to take place to allow this data binding. @BindSimple
has a converter attribute which by default is set to SafeBeanUtilsConverter
. This converter implementation,
which also uses Jakarta Commons BeanUtils, is able to perform the simple necessary type conversion without
any additional configuration. The equivalent "longhand" way of expressing this is shown below:
@BindSimple(expression = "booking.title", converter = SafeBeanUtilsConverter.class) public String getDays() { return days; }
More advanced conversion requirements may require either a custom Converter
implementation or
even a custom converter annotation.
Data binding is bi-directional. An inward binding is a binding from form property to domain object and an
outward binding is in the reverse direction. Inward bindings will take place when a form is submitted.
Outward bindings take place when a form is rendered, but only if there are no form errors present.
In this case, the form needs to be re-rendered with the user supplied values present.
Also, note that the form's domain object must be populated before binding can occur. If necessary, this
can be done in the form submission controller's preBind()
method, if necessary.
The JSP markup for using the @BindSimple
annotation is very simple.
Any Struts tag which maps directly to a single
property value, such as html:text
and html:textArea
, can be used.
An example is shown below:
<html:text property="days" size="2" />
The purpose of @BindSelect
is to allow for binding of user selections.
Suppose our HolidayBooking
object
also contained a property leaveType
of type LeaveType
,
which could be "annual vacation", "sickness" (OK, not really a holiday)
or "personal day".
This could be represented in the database using a reference data table, and presented
on the user interface using a drop down menu or set of radio buttons.
An example of form implementing a @BindSelect
in this case is shown below:
private Collection<LeaveType> leaveTypes; private HolidayBooking booking; private String selectedLeaveType; @BindSelect(targetBeanExpression = "booking.leaveType", lookupMapExpression = "leaveTypes", targetBeanIdProperty = "leaveTypeId", idClass = Integer.class) public String getSelectedLeaveType() { return selectedLeaveType; } public void setSelectedLeaveType(String selectedLeaveType) { this.selectedLeaveType = selectedLeaveType; } public Collection<LeaveType> getLeaveTypes() { return leaveTypes; } public void setLeaveTypes(Collection<LeaveType> leaveTypes) { this.leaveTypes = leaveTypes; }
In the example we see the ingredients required for a @BindSelect
operation.
String
property which records the selection.
This property is selectedLeaveType
.<html:optionsCollection/>
or <html:radio/>
tags, for example.
In our example, the property is named leaveTypes
and is a java.util.Collection
,
which could represent a List
or Set
. It could also be implemented
as a java.util.Map
, although the JSP markup would need to change to accommodate this.
Typically, the collection property will also be accessible via the form, and will need to be populated,
usually using some code in the action bean,
before the page containing the form is rendered.
LeaveType
to hold the selection.
In our example, this property is called leaveType
.When doing an inward binding in our example, the @BindSelect
operation will:
selectedLeaveType
Integer
. It knows to do this from the value of the @BindSelect
idClass
attribute@BindSelect
lookupMapExpression
to find the map containing available LeaveTypes
Integer
as a key to look up the appropriate LeaveType
instancebooking
object using the expression obtained from the @BindSelect
targetBean expressionDoing an outward binding is slightly simpler. Here, it goes throught the following steps:
leaveType
instance from the domain object using the targetBeanExpression
targetBeanIdProperty
to obtain the ID field for this objectselectedLeaveType
propertyThe @BindSelect
works well for reference data,
which often has a numerical ID as well as a name field and other descriptive information attached.
The last piece of the puzzle is the JSP markup. Below, we show two examples, one using a drop-down menu,
and another using an HTML select.
With the drop-down menu, we use the html:optionsCollection
tag to present the options, shown below:
<html:select property="selectedLeaveType" size="1"> <option value=""></option> <html:optionsCollection property="leaveTypes" label="leaveTypeName" value="leaveTypeId" /> </html:select>
In the radio button example, we iterate over leave types using the JSTL forEach
tag. In our example, the
value for the radio button will be the key for each Map
entry iteration.
<c:forEach var="item" items="${holidayBookingForm.leaveTypes}"> <c:set var="itemId" value="${item.leaveTypeId}" /> <html:radio property="selectedLeaveType" value="<%= pageContext.getAttribute("itemId").toString() %>" /> <c:out value="${item.leaveTypeName}" /><br/> </c:forEach>Notice the use of the scriptlet in the html:radio tag. We could make this work slightly more elegantly by using the EL version of the Struts html:radio tag, in which case the above example would like as shown below:
<c:forEach var="item" items="${holidayBookingForm.leaveTypes}"> <html-el:radio property="selectedLeaveType" value="${item.leaveTypeId}" /> <c:out value="${item.leaveTypeName}" /><br/> </c:forEach>
Much of the time, definining an explicit converter is not necessary. A notable exception is for date fields. In this case, a
textual pattern must be identified which can be used to perform the conversion. For example, the JDBC Date
object has the
format yyyy-MM-dd
. In this case, it is possible to parameterize the binding annotation with the converter class.
It would be a bit tedious, however, if a new converter class needed to be created for each date pattern which could be used.
Strecks comes with a DatePaternConverter
which is itself parameterized by the textual conversion pattern to be used.
The problem with this class is that it cannot be used within the @BindSimple
annotation,
because @BindSimple
defines no way of parameterizing converters. That's not its job.
This roles is instead performed by the @ConvertDate
annotation. An example is shown below:
private String startDate; @BindSimple(expression = "booking.startDate") @ConvertDate(pattern = "yyyy-MM-dd") public String getStartDate() { return startDate; }
Currently, the only supplied conversion annotation is the @ConvertDate
annotation.
However, the framework exists to create
your own conversion annotations, allowing any custom converters to be parameterized as required.