Input controls part 2 – Rendering and parsing
One of the biggest problems in Web development is that usually the code responsible for rendering data is separated from the code used for parsing and validating data. In most cases the rendering is performed in a JSP page, either by directly embedding string representation of the value in HTML or by a special JSP tag that renders the value according to information provided with attributes on the tag. When the user submits the form the values are usually pushed into a JavaBean or POJO by an interceptor which takes the parameters from the request, looks for an appropriate setter method and then converts the value to fit the setter’s value data type.
This procedure has several disadvantages that are best understood by the following example:
Consider a phone number which is stored in the database in a single field. On the form however
you want to display and edit the phone number in three individual text fields: the first for the
country code, the second for the city code and the last for the extension code which are separated
from each other using a dash. The first problem with traditional Web application frameworks is,
that since there is no tag for such behaviour you have to write all the HTML code for those three
separate input tags plus the label in your JSP. Even worse however is, that when the form is
submitted three individual fields are supplied that you have to assemble to a single value again.
Now for this you have several options which are all quite complicated for such a simple task. If you
don’t want to provide your own interceptor you will have to provide corresponding setters on your JavaBean and – if you don’t want to rely on the order by which these setters are called - you’d
have to add additional post processing code that will put them together again. Still the assembly
code must rely on the rendering code which is in the JSP and thus not in the same place. As a third
problem you’ll find the task of validation – for which you might have to write your own validation
class. At last, all this has to be done for every single phone number field you have in your
application. Imagine you have several phone number fields then handling
all of them becomes quite a nasty
problem.
With Empire-Struts2-Extensions the task of rendering and the task of parsing and validating form
values are now put together in one simple class: The input control. The input control is an abstract
base class for which you can easily provide your own implementation if necessary by only implementing
three simple methods: renderText()
, renderInput()
and getFieldValue()
.
For the most common field types there are already implementations provided such as text, select, password, phone, checkbox and more.
The major differences to traditional processing are:
- The input control has access to field metadata and can thus perform rendering, parsing and validation without additional redundant information provided in the view. However should you ever need it, you may as well override these attributes in your JSPs.
- The input control class distinguishes between read only and editable rendering and can thus also be used for list views for example. Consider a field whose value you have to format in a particular way for display. Simply write your own input control class and assign the control type to the corresponding column. Wherever a value for this field is displayed with any of the Empire tags it will be formatted correctly – without any change or additional attributes in the JSPs.
- For processing submitted form data the input control uses a pull rather than the usual push approach. This allows the input control to combine two or more fields to one value. This is e.g. used for checkboxes where an additional hidden value is used to detect whether the checkbox has been unchecked. Since the rendering code is in the same object both the splitting and the assembly can easily be kept consistent.
The following graphic shows how the InputControl is the interface between the view and the model:
The list view JSP uses the <e:value>
tag (or alternatively an <e:td>
tag which extends <e:value>
) to render the field's value for
display. The form view on contrary uses the <e:control>
tag to
render a form input control which may be used to change the value.
When the form is submitted, an action support class iterates through
all fields and gives the corresponding InputControl the opportunity
to pull the value(s) off the request and to perform parsing and
validation. If successful the field value is written back to the
record. For all three tasks access to data and metadata is
necessary.
Writing a custom input control
The following example shows, how easily a custom input control type can be created and applied.
Step 1: Write your input control class.
Create a new class, derive it from InputControl and provide an implementation
for the
three methods renderText()
, renderInput()
and getFieldValue()
.
Remember that your class must be fully stateless since one single instance is
shared for all fields and threads. However all information you
require
is provided with the parameters. Here is an outline of what your
class may look like:
public class CustomInputControl extends InputControl { @Override public Object getFieldValue(String name, RequestParamProvider request, Locale locale, DBColumn column) { … } @Override public void renderText(HtmlWriter writer, ValueInfo vi) { … } @Override public void renderInput(HtmlWriter writer, ControlInfo ci) { … } }
Step 2: Register your class with the InputControlMananger.
To make the Empire-Struts2-Extensions capable of using your input control class you must register it with the input control manager as follows:
// Register Control InputControlManager.registerControl("custom", new CustomInputControl());
Step 3: Assign your input control type to database columns
Finally, assign your control type to database columns. To do
this, use the setControlType()
function which is provided with the
DBColumn
objects. For those fields, your custom input control class will now
be used for rendering and form request processing.
MyDatabase db = getDatabase(); db.MYTABLE.MYCOLUMN.setControlType("custom");