|
Generic Approach for Back-Button-Friendly Web Rowset Paging
Oracle JDeveloper Tip
Generic Approach for Back-Button-Friendly Web Rowset Paging Author: Steve Muench, ADF Development
Team Date: July 26, 2004
| NOTE: |
You can
download the PagingActionExample.zip
workspace containing the example project that illustrates this feature in use.
You'll need a JDeveloper database connection named scott
defined for the familiar SCOTT schema
|
Web Rowset Paging and the Browser Back Button
Over on
this
thread in the
OTN JDeveloper
Forum, a user asks about alternatives to ADF's
PreviousSet and NextSet built-in actions
for paging through a data collection. One of his issues with the way that the
PreviousSet and NextSet actions work is
that they are relative to the current starting row in the range, which can
sometimes surprise users if they go backwards through the data by using the
back button.
For example, suppose we're showing users 35 rows of
query results, 10 at a time. This means that we've created an ADF iterator
binding in our browse page's binding container with Range
Size of 10, and a range binding to support rendering the data as a
table, as well as filtering and ordering that way that the columns appear. If
we use the default PreviousSet and
NextSet actions, and the user performs the following
sequence of steps, then they may be surprised at the final step due to their
using the browser back button in between.
Imagine that the
user:
- Starts by looking at rows 1-10
- Presses
(NextSet)
- Sees rows
11-20
- Presses the browser back
button.
- Sees rows 1-10 again (from local
browser page cache)
- Presses the
(NextSet) button to go forward again.
After step 6, having clicked the
(NextSet) button while visually seeing the cached browser
page showing rows 1-10, the user will see one of two results, either of which
is likely to surprise them:
-
If the page
contains the ADF bindings state token, and the developer
has left the default setting of True for the
Enable Token Validation property on their binding
container, the user will see:
JBO-33035: Row currency has changed since the user interface was rendered.
The expected row key was oracle.jbo.Key[NNNNN]
- If the page does not
contain the ADF bindings state token, or if the developer has set
the Enable Token Validation property on their binding
container to
False at design time, then the user will see
rows 21-30.
Both "surprises" occur
because at step 6 above, the user pressed the browser back button to see the
previous page full of rows, instead of pressing the
(PreviousSet) button. When they press the browser back
button they see the previous page from the browser's local page cache. However,
since this involved no interaction with the web server the ADF iterator
binding's current notion of the first row in the range — as well as the current
row in the rowset — remains unchanged.
Of course, if the user had
used the (PreviousSet) button to go backward instead of
the browser back button, everything would have worked as
expected.
The ADF Binding Token
The
binding
token is added to your page by the ADF data control palette during drop
operations involving an HTML form. For example, dropping an Input
Form, or a Button with Form the ADF design time
will add the following field into your form:
<input type="hidden"
name="<c:out value='${bindings.statetokenid}'/>"
value="<c:out value='${bindings.statetoken}'/>"/>
It's name and
value are provided by EL expressions that access
statetokenid and statetoken properties on
the current binding container. The binding token validation can be controlled
by the Enable Token Validation property on any binding
container, whose True or False value you
can set in the Property Inspector after clicking on the
binding container name in the UI Model tab of the
Structure Window.
The binding token is used to
prevent duplicate form submission and to validate that the state of the
iterators in the binding container are what your submitting web form expects
them to be. It is validated — if the token exists in the submitted HTTP request
and the Enable Token Validation property is set to
True (the default) — during the prepareModel() phase of the
ADF page lifecyle.
An Example Solution
A solution is to keep track of the current page number in
a browser hidden field, or in a URL parameter, and then use this current page
number — along with an appropriate event like NextPage,
PreviousPage, or GotoPage — to calculate
which page to scroll to next. This approach sets the next page the user sees
relative to the current page that they see — irrespective of whether they got
to that page via the browser back button, or the
(PreviousSet) button — instead of scrolling the page
relative to the ADF iterator's current notion of the starting row in the
range.
I cobbled together a PagingActionExample.zip sample
application workspace that illustrates this approach.
Some
interesting points to note about the sample code:
-
The PagingDataForwardAction class in the
test.controller package extends the base
DataForwardAction for ADF data pages with generic support
for handing events named NextPage,
PreviousPage, GotoPage, and
UpdateRangeSize. As expected from the
Understanding
ADF DataAction Event Handling section of the
ADF
Data Binding Primer and ADF/Struts Overview whitepaper, it accomplishes
this by having onNextPage(),
onPreviousPage(), onGotoPage(), and
onUpdateRangeSize() methods with the expected signature.
Notice that the core routine that accomplishes the paging uses the
ADF RowSetIterator API called scrollToRangePage(int
n) to scroll the display to the
nth page.
If the
changeCurrentRowToNewPage() method returns
true it will also move the current row to be on the current
page. By default, scrolling the visible range of rows that the user can see is
like sliding the scrollbar of an Excel spreadsheet. Your eyes see different
rows, but the current row is where it was before.
If the
retrieveOnlyCurrentPageFromDatabase() method returns
true it will use the
ADF
view object feature called Range Paging to fetch and cache only the
current page worth of rows from the database.
- The
PagingTable.jsp page illustrates a possible
implementation of a "generic table paging widget" that can be reused in any JSP
page that has an ADF range binding.
- The
browseDepts.jsp and browseEmps.jsp pages
illustrate two examples of reusing this generic
PagingTable.jsp paging "widget", passing two parameters that
are needed: the name of the range binding in the page's binding container to
use for the paging behavior, and the name of the current page to use for the
post-back URL.
- The corresponding browseDepts
and browseEmps Struts data action mappings are setup to use the
test.controller.PagingDataForwardAction class instead of the
default DataForwardAction
class.
Hopefully this small example
application will help you think about how you'd like to tackle web-based paging
of database query results in your own ADF-based
applications.
|