UIX Developer's Guide |
![]() Contents |
![]() Previous |
![]() Next |
This chapter contains the following sections:
Web pages typically support a variety of actions, such as entering and submitting form data and navigating to different pages. Many web pages also support another type of action, which is to allow the user to make modifications to the contents of the web page itself without actually navigating to a different page. Some examples of such actions include:
All of these actions are similar in that they result in the same page being re-rendered in a slightly different state. Ideally, these changes should be implemented as seemlessly as possible, so that the user does not experience a loss of context which could distract from the task at hand.
Developers that want to add such behaviors to their web pages are often faced with a difficult decision. All of these actions can be implemented using a very simple solution: by refreshing the entire page in response to the user interaction. However easy, this solution is not always desirable. The full page refresh can be slow, giving the user the impression that the application is unresponsive. Another option is to implement such actions using JavaScript (or other client-side scripting technologies). This results in faster response times, at the expense of more complex, less portable code. JavaScript may be a good choice for simple actions, such as updating an image. However, for more complicated actions, such as scrolling through data in a table, writing custom JavaScript code can be a very challenging undertaking.
UIX provides another solution which avoids some of the drawbacks of the full page refresh and custom JavaScript solutions: partial page rendering. The UIX partial page rendering functionality provides the ability to re-render a limited portion of a page. As in the full page render solution, partial page rendering sends a request back to the application on the middle-tier to fetch the new contents. However, when partial page rendering is used to update the page, only the modified contents are sent back to the browser. UIX automatically merges the new contents back into the web page. The end result is that the page is updated without custom JavaScript code, and without the loss of context that typically occurs with a full page refresh.
The partial page rendering process breaks down into three main areas: the partial page event, the partial page rendering pass, and the partial page replacement.
The partial page event is the request that the browser sends to the
application to request new contents. Partial page events are very
similar to their full page event equivalents. For example, when the
user navigates to a new record set in a table
bean,
a goto
event with a value
event parameter
is sent to the application regardless of whether the event is a
partial or full page event. There are two important differences
between partial and full page events. First, partial page events
specify partial page rendering-specific event parameters which are
not present on the full page event equivalents. For example, partial
page events may include an event parameter which identifies the set
of partial targets, or nodes that should be re-rendered. The second
difference between partial page events an full page events is how
the events are sent.
Unlike full page events, partial page events must be sent in a way which does not force the browser to reload the current page. To implement this capability, UIX partial page rendering uses a hidden inline frame (iframe) as a communication channel between the browser and the web application running on the middle-tier. Partial page events are sent by forcing a navigation in the hidden iframe, which in turns causes a request to be sent to the application on the middle-tier. Since the iframe is hidden, the process of sending a partial page event and rendering partial page contents can take place behind the scenes, without discarding the contents of the current page.
When the partial page event is received by the application, the application
responds by determining the set of partial targets to render and performing
the partial page rendering pass. The partial page rendering pass is
similar to a full page rendering pass. In both cases, the UINode tree
is traversed by calling render()
on each node in the tree.
However, in the partial page rendering case, only the contents generated
by the partial targets are actually sent back to the browser. All other
contents are dropped. So, although the scope of a partial page rendering
pass and full page rendering pass are similar in the number of UINodes
that are rendered, the partial page response is generally much smaller,
since only the modified contents are sent back to the browser.
The final part of the partial page rendering process is the partial page replacement. When the browser receives the partial page response, the new contents for each partial target node are copied from the hidden iframe into the main browser window, replacing the existing contents for each target node. So, for example, in the table navigation case, rather than replacing the entire page, just the contents of the table itself are replaced. The browser reflows in response to the modifications, displaying the new contents to the user without fully re-rendering the entire page.
To recap, the sequence of steps which occur during a partial page render are:
Note that the partial page rendering process requires no custom JavaScript code.
The partial page rendering implementation requires the ability to modify arbitrary content via the browser's DOM APIs. At the moment, partial page rendering is only supported on the following browsers:
On all other platforms, UIX automatically reverts to full page rendering. In the future, partial page rendering should be supported on a wider range of browsers, including IE 5.0. However, due to Netscape 4.x's limited DOM support, partial page rendering will not be supported on Netscape 4.x.
One other note: Since the client-side DOM modifications used by
partial page rendering may cause problems for assitive technologies
such as screen readers, partial page rendering is only used when the UIX
accessibility mode is explicitly set to
AccessibilityMode.INACCESSIBLE_MODE
.
It may be the case that partial page rendering could actually be
beneficial to users that read UIX pages with screen readers,
since these users also suffer from the loss of context that
comes with a full page refresh. Further research
is needed to determine whether partial page rendering can be used
in a way which is compatible with assistive technologies. In the meantime,
full page rendering is used for all accessibility modes other than the
inaccessible mode. For more information on how applications can specify
the accessibility mode for a particular user, see the section on
setting the accessibility mode.
Four UIX components have been enhanced to leverage partial page rendering:
the table
, hideShow
and the
hideShowHeader
, subTabLayout
. Each of these
components supports
two new attributes: partialRenderMode
and
partialTargets
. The partialRenderMode
attribute
controls whether or not partial page rendering is enabled for a
particular component. This attribute can be set to one of three values:
none
, self
or multiple
.
The default value is none
, which indicates that partial page
rendering is disabled. Setting the partialRenderMode
attribute
to self
indicates that the component itself should be updated
using partial page rendering. This is the most common case. The
multiple
mode is used in conjunction with the
partialTargets
attribute to specify that multiple partial
targets should be re-rendered in response to each partial page event.
So, for example, to indicate that a table should refresh itself
using partial page rendering, the partialRenderMode
can be set as follows:
<table id="table-id" partialRenderMode="self">
To indicate that several dependent fields need to be updated any time
the table is updated, use the multiple
mode with
the partialTargets
attribute:
<table id="table-id"
partialRenderMode="multiple"
partialTargets="dependent-id1 dependent-id2 dependent-id3">
Note that when specifying multiple partial targets, the ID of the
component which fires the partial page events is implicitly included
in the list of partial targets. Thus, in the above example, the
ID of the table ("table-id") should not be included in the
partialTargets
value, since this ID is automatically
considered to be a partial target.
In the case of the table
component, setting the
partialRenderMode
to self
or
multiple
turns on
partial page rendering for the following actions:
For the hideShow
and hideShowHeader
components,
partial page rendering is used to optimize both the hide and show actions.
The subTabLayout
component uses partial page rendering to
update its contents when switching between tabs.
In addition to setting the partialRenderMode
attribute,
there are several other requirements that must be met in order to
enable partial page rendering. These requirements are described in the
following sections.
A body
bean must be present in the UINode
hierarchy in order to enable partial page rendering. Any UIX page which
leverages partial page rendering should have a structure similar to the
following:
<page xmlns="http://xmlns.oracle.com/uix/controller">
<content>
<document xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- The body bean is required -->
<body>
<contents>
<!-- Components which fire partial page events can go here -->
</contents>
</body>
</contents>
</document>
</content>
</page>
If no body
is present, partial page rendering will fail.
For most uiXML pages, body
will be automatically added
for you - only pages that use document
need an explicit
body
element. Java and UIX JSP developers, however,
must explicitly use BodyBean
and <uix:body>
respectively.
Any node which might be re-rendered via partial page rendering must
specify a value for the ID attribute. For example, the following
code is not sufficient to enable partial page rendering for the
hideShow
bean:
<hideShow partialRenderMode="self">
The ID attribute must also be specified as follows:
<hideShow id="some-unique-id" partialRenderMode="self">
The ID serves several important purposes during the partial page rendering process. First, the partial page event identifies the set of partial targets to re-render by ID. During the partial page rendering traversal, the partial target nodes are located in the UINode tree by examining the ID of each node. Finally, when the partial page contents are sent back to the browser, the ID is used to locate the target node in the browser's DOM tree for the partial page replacement.
Partial page rendering is only enabled when it is known that the end user
does not require accessibile content. This information is conveyed by
the AccessibilityMode
, which is specified via the
Configuration.ACCESSIBILITY_MODE
property. This property
can be set with a single method call:
ConfigurationImpl configImpl = ...
configImpl.putProperty(Configuration.ACCESSIBILITY_MODE,
AccessibilityMode.INACCESSIBLE_MODE);
However, UIX Controller clients can specify this information declaratively
via the uix-config.xml
configuration file. Simply add a new
configuration
element as follows:
<configuration name="noADA">
<accessibility-mode>inaccessible</accessibility-mode>
</configuration>
An override of UIXPageBroker.getConfigurationName()
can then
specify which configuration to use on a per-request basis:
protected String getConfigurationName(
BajaContext context,
Page page)
{
if (_useInaccessibleConfiguration(context))
return "noADA";
return super.getConfigurationName(context, page);
}
In this example, the _useInaccessibleConfiguration()
method
must determine whether or not to render inaccessible content for the
particular request. The implementation of such a method would be
application-specific. For example, an application which has a persistent
store for user preferences could retrieve the user's preferred accessibility
mode from a repository. Applications which do not store user preferences
in a repository could use a cookie or an HttpSession
value to track whether or not the user requires accessible content.
For more information on working with the Configuration
API, see the Configuration
chapter.
During partial page replacement, the newly generated contents are
copied from the hidden iframe into the parent window of the iframe
for display. If the new contents include any scripts, the scripts
are explicitly executed in the parent window as well. This is necessary
so that any functions or variables that are defined by the scripts
will be available from the parent window. However, executing scripts
in the parent window can cause problems if the scripts themselves
generate content. Scripts which call document.write()
or document.writeln()
must not be executed in the parent
window, as these calls will cause the contents of the parent window
to be completely replaced.
Since UIX can not automatically determine whether or not a particular
script generates content (that is, calls document.write()
or document.writeln()
), clients must explicitly identify
such scripts. This can be done via the script
bean's
generatesContent
boolean attribute. The
generatesContent
attribute defaults to false, which
indicates that the script does not generate content. Clients that use
partial page rendering should be sure to set the attribute to true for
any scripts which call document.write()
or
document.writeln()
, for example:
<!-- Explicitly specify generatesContent="true", since
this script calls document.write() -->
<script generatesContent="true">
document.write("Hello, World!");
</script>
Failure to identify scripts which generate content in this way can result in a partial page request replacing the entire page with just the contents generated by the script.
Once partial page rendering is enabled, components use the hidden iframe to send partial page events to the application. The UIX Controller automatically processes these events by rendering any partial target nodes that are specified by the event. Thus, UIX Controller clients are not required to make any changes in order to handle partial page events. Existing UIX Controller event handlers can be reused across both partial page and full page events. However, applications which use custom controllers must be enhanced to detect partial page events, determine the set of partial targets to render, and render the requested contents.
Applications which leverage partial page rendering must be able
to distinguish between partial page and full page events. This
responsibility belongs to the application's controller. Controllers
can distinguish between partial page and full page events by
calling the UIX PartialPageEventUtils.createPartialPageContext()
method for each request (specifying either the HttpServletRequest
object or a oracle.cabo.share.data.ServletRequestParameters
object). This method examines the incoming request to determine whether
or not the request is a partial page event. If the request is a partial
page event, a PartialPageContext
object is created and
returned. The PartialPageContext
object encapsulates any
state associated with the partial page render. For example, the
PartialPageContext
defines the set of partial targets to render.
In the case of a full page event, createPartialPageContext()
returns null. Thus, controllers can distinguish between full page and
partial page events simply by checking the value returned by this
method. In the full page event case, the controller can proceed with
its normal processing. In the partial page case, the controller needs
to perform additional work to ensure that only the desired partial
targets are rendered.
Each partial page event may specify one or more partial targets that
need to be re-rendered. createPartialPageContext()
automatically initializes the PartialPageContext
with
the set of requested targets. However, there are cases where the
application may want to modify the set of partial targets to render
when processing a particular event. For example, a page which contains
both a table and a graph image may need to re-render both the table
and the graph any time the table is updated. In this case, even
though the partial page event may only specify that the table should
be re-rendered, the application can intervene and explicitly modify
the set of partial targets so that the graph is rendered as well.
The PartialPageContext
includes methods for adding
and removing partial targets: addPartialTarget()
and
removePartialTarget()
. These methods can be called
prior to rendering to modify the set of partial targets to render.
For UIX Controller clients, the most obvious place where clients
might want to call these methods is from a UIX Controller event handler.
Clients can obtain the PartialPageContext
from a UIX event
handler by calling the
PartialPageEventUtils.getPartialPageContext()
method.
Once the set of partial targets has been determined and any
application logic has been executed, it is finally time to respond
to the partial page event by rendering the needed contents. Performing
a partial page render is very similar to a full page render. The only
difference is that the PartialPageContext
must be set on
the RenderingContext
prior to rendering. This can be
accomplished using the PartialPageUtils.setPartialPageContext()
method. The UIX Controller automatically calls this method when
processing partial page events, so UIX Controller clients do not
need to perform any special work.
Another option for applications with custom controllers is to
call the PartialPageUtils.renderPartialPage()
convenience
method, which both sets up the PartialPageContext
on the
RenderingContext
and performs the render.
So, instead of:
node.render(renderingContext);
The application should call:
PartialPageUtils.renderPartialPage(renderingContext,
node,
partialPageContext);
Note that in both the partial page case and the full page case, the
same root node of the UINode tree must be rendered. That is, even
though the partial page rendering pass may only generate content for
the limited set of partial target nodes, the entire UINode tree must
be traversed. Also, as a convenience renderPartialPage()
can be called with a null PartialPageContext
, in which case
a normal full page render will be performed.
If errors occur while processing a partial page event, it may not be sufficient to simply render the requested partial target nodes. Applications may need to provide additional information about the error to the end user. Depending on when the error is detected, one of two options can be used to provide more information. If the error occurs before rendering has commenced, the application can display a new error page. For errors which occur during rendering, navigating to an error page is not an option. In these cases, it is possible to provide additional information by enhancing the set of partial targets to include nodes that were not specifically requested in the partial page event.
In order to render an error page in response to a partial
page event, it is not sufficient to simply call
UINode.render()
on the UINode
tree for the error page. Remember
that partial page events are sent via a hidden inline frame. Any
contents that are sent back to the browser are first displayed in the
inline frame and must be explicitly copied into the parent document
window in order to be made visible to the end user. So, calling
UINode.render()
on a new UINode tree
may generate the desired error page contents, but the contents are
not made visible. Instead, applications must call the
PartialPageUtils.renderFullPage()
method to render a new full page of content.
The renderFullPage()
method ensures that
the entire contents of the UINode tree are sent back to the
browser and are copied from the hidden iframe into the parent window for
display.
UIX Controller-based applications can achieve the same result simply
by returning a different Page
from the
EventHandler
which handles the partial
page event. In this case, the UIX Controller automatically calls
renderFullPage()
on behalf of the application.
Once rendering has started, it is too late to navigate to a new
page to display error information. However, when errors are
detected during the partial page rendering pass, it is possible
to include error information in the partial page response
by adding new partial targets. For example, if an error occurs
while querying data to populate a partial target, the application
can display an error message in a MessageBox
bean even if the id
of the MessageBox
bean was not explicitly included in the set of
requested partial targets. The
PartialPageContext.addPartialTarget()
method can be called to indicate that a new UINode, specified by
its ID attribute value, should be added to the set of nodes that
are rendered during the partial page rendering pass.
There are some requirements that must be met in order for the
newly added partial targets to be successfully rendered and displayed.
The most obvious requirement is that the ID attribute must be specified
on any UINode that is a potential partial target. Also, the new
partial target must be rendered after
addPartialTarget()
is called. In general
this means that UINodes that are used to display error information
should be placed strategically beneath any potentially problematic partial
targets in the UINode tree. The final requirement is that the new
partial target must have been rendered in some form on the previous
full render of the current page. At a minimum, there must be some
node in the client-side DOM tree with the ID of the error node, even
if this node does not display any content. If a node with the
required ID can not be found in the client-side DOM tree, the
error message contents are not copied into the document.
So far we have seen how to enable partial page rendering for
table
, hideShow
,
hideShowHeader
and subTabLayout
components, but what
about the other proposed uses that were described earlier in this
chapter? The partial page rendering architecture is designed to
support the ability to fire partial page events in response to
arbitrary user actions, such as clicking on the link or button,
or selecting an item from a choice box. However, as of yet UIX
does not expose public APIs for firing such events. Future versions
of UIX will provide such APIs, which should greatly expand the
number of cases where partial page rendering can be used to
optimize user interaction. In the meantime, we encourage UIX clients
to take advantage of partial page rendering for the table
and hideShow
components.