Written By Duncan Mills, Oracle Corporation
February 2005
Introduction
Oracle JDeveloper 10g, using the Oracle Application Development Framework (ADF),
provides the JSP page developer with simple ways to produce various UI artifacts
such as drop-down lists and radio groups which are bound to the business service
in use. I have documented the steps to do this in previous How To documents,
for example: Creating
a Databound Drop Down List in Oracle JDeveloper 10g). In this article though
I'll be looking a a slightly different scenario, that of binding a checkbox.
Traditionally in an Oracle database, the kind of boolean choice that you want
to expose through a checkbox has been defined as a simple character column on
the table for instance VARCHAR2(1), using the valid values "Y" or
"N" to indicate the boolean state. The technique outlined in this
paper is based on this scenario, but will work just as well with any kind of
boolean storage, for instance a CHAR or NUMBER. In this example we'll be working
with an employee record that has an addition field called CONTRACTOR. In the
database schema this is defined as being NOT NULL, and has a check constraint
to enforce valid values of "Y" or "N". Before we proceed
I'll assume that you've already defined your business service and the relevant
data control is available in the Data Control Palette.
Creating the UI
The simplest way to create both the model binding and the UI for the checkbox
is to drag and drop the field in question into your input form as normal input
field. If you created the input form by dragging and dropping the whole collection
as an "Input Form" from the Data Control Palette then of course this
will have already been done. To convert the field from a normal text input to
a checkbox, switch to Source view in the JSP editor and look for the relevant
column name. So in my example I'll just search for the String <html:text
property="Contractor"/>. To convert the UI to a checkbox you'll
need to change the html:text to html:checkbox. You also need to
tell the tag what the checked value is, in this case "Y", using
the value attribute. The altered tag will now look like this:
<html:checkbox property="Contractor" value="Y"/>
If you go ahead and run the page at this point, everything will appear to work
correctly, existing rows in the collection will be shown either checked or unchecked
as expected. However, if you start to test a little you'll begin to see some
problems:
If you create a new record with the checkbox unchecked, then you'll get
a constraint violation error when you commit.
If you update an existing record by un-checking the box, that change will
apparently get ignored and when the screen is refreshed the box will show
as checked again.
What's going on here?
The problem we're encountering here is down to a peculiarity of checkboxes
in HTML UIs. The value of a checkbox is only sent to the server if it is checked,
so for an unchecked checkbox no value is sent. As such, the two problems highlighted
above happen because the ADF model layer is simply not been notified of the
checkbox status at all.
Handling an Unchecked Checkbox
So let's look at what you have to do to handle this behavior. Fortunately it's
a simple thing to detect, if you know it's likely to happen on a particular
page:
In the Struts Page flow diagram, right mouse click on the DataPage in question
and choose Go To Code to create a DataForwardAction subclass.
In the Java code editor for the code, choose Tools > Override Methods
and select the processUpdateModel() method to implement. This method
maps the contents of the Struts Form Bean used by ADF back into the underlying
business service. The processModelUpdate() code will initially just have a
call to the superclass to process the mapping in the normal way.
Now we have to check to see if the checkbox is actually included in the
request parameters. If it is included, then we know it's checked in the UI
and we have to do nothing. If it is not included then we have to explicitly
set the unchecked case into the ADF Form Bean for the normal update process
to handle.
Here's the code for the sample scenario:
protected void processUpdateModel(DataActionContext ctx) { //First look for the request parameter Object cbInRequest = ctx.getHttpServletRequest().getParameter("Contractor");
//We only need to do anything if it was not submitted if (cbInRequest == null) { // Get hold of the Form Bean containing the record BindingContainerActionForm updateForm = (BindingContainerActionForm)ctx.getActionForm(); //Get the binding for our particular column JUCtrlAttrsBinding checkBoxBinding = (JUCtrlAttrsBinding)updateForm.get("Contractor"); //Reset that explicitly to the *unchecked* value (N in this case) checkBoxBinding.setAttribute(0,"N"); }
//Now let the model update progress using the data in updateForm as normal super.processUpdateModel(ctx); }
The above code will need some import statements added to the class for BindingContainerActionForm
and so on. JDeveloper will prompt you for these as the type is entered, press
Alt-Enter to action the auto-import when prompted.
The above code can usefully be refactored into a small utility method that just
takes the name of the field and the unchecked value. If you already have your
own framework subclass of DataForwardAction (something I'd generally recommend)
then this utility method would be a good candidate for storing in that class
for reuse.
Finally, note that the code shown above is deliberately simplified for clarity.Your
own code should be defensive and check for things like checkBoxBinding being
not null and so forth.