3 Using ADF Faces
Architecture
This chapter outlines the major features
of the ADF Faces client-side architecture.
This chapter includes the following
sections:
■ Section 3.1,
"Introduction to Using ADF Faces Architecture"
■ Section 3.2,
"Listening for Client Events"
■ Section 3.3,
"Adding JavaScript to a Page"
■ Section 3.4,
"Instantiating Client-Side Components"
■ Section 3.5,
"Locating a Client Component on a Page"
■ Section 3.6, "Determining
the User’s Current Location"
■ Section 3.7,
"Accessing Component Properties on the Client"
■ Section 3.8,
"Using Bonus Attributes for Client-Side Components"
■ Section 3.9,
"Understanding Rendering and Visibility"
3.1
Introduction to Using ADF Faces Architecture
The ADF Faces rich client framework (RCF) provides
many of the features you need to
create AJAX-type functionality in your web
application, all built into the framework. A
key aspect of the RCF is the sparsely populated
client-side component model. Client
components exist only when they are required, either
due to having a
clientListener
handler registered
on them, or because the page developer needs
to interact with a component on the client side and
has specifically configured the
client component to be available.
The main reason client components exist is to provide
an API contract for the
framework and for developers. You can think of a
client-side component as a simple
property container with support for event handling.
Because client components exist
only to store state and provide an API, they have no
direct interaction with the DOM
(document object model) whatsoever. All DOM
interaction goes through an
intermediary called the peer. Most of the inner workings of the framework are
hidden
from you. Using JDeveloper in conjunction with ADF
Faces, you can use many of the
architectural features declaratively, without having
to create any code.
For example, because RCF does not create
client components for every server-side
component, there may be cases where you
need a client version of a component
instance. Section 3.4, "Instantiating
Client-Side Components," explains how to do this
declaratively. You use the Property
Inspector in JDeveloper to set properties that
determine whether a component should be
rendered at all, or simply be made not
visible, as described in Section 3.9,
"Understanding Rendering and Visibility."
Other functionality may require you to use
the ADF Faces JavaScript API. For
example, Section 3.5, "Locating a Client
Component on a Page," explains how to use
the API to locate a specific client-side
component, and Section
3.7, "Accessing
Component Properties on the
Client," documents
how to access specific properties.
3.2
Listening for Client Events
In a traditional JSF application, if you want to
process events on the client, you must
listen to DOM-level events. However, these events are
not delivered in a portable
manner. The ADF Faces client-side event model is
similar to the JSF events model, but
implemented on the client. The client-side event model
abstracts from the DOM,
providing a component-level event model and lifecycle,
which executes independently
of the server. Consequently, you do not need to listen
for click events on buttons.
You can instead listen for AdfActionEvent events, which can be caused by key or
mouse events.
Events sent by clients are all subclasses of the AdfBaseEvent class. Each client event
has a source, which is the component that triggered
the event. Events also have a type
(for example, action
or dialog), used to determine which listeners are interested in
the event. You register a client listener on the
component using the
af:clientListener
tag.
For example, suppose you have a button
that, when clicked, causes a "Hello World"
alert to be displayed. You would first
register a listener with the button that will
invoke an event handler, as shown in Example 3–1.
Example
3–1 Registering a Client Listener
<af:commandButton text="Say Hello">
<af:clientListener method="sayHello"
type="action"/>
</af:commandButton>
Because the button
has a registered client listener, the
framework will
automatically create a client version of the
component.
Next, implement the handler in a
JavaScript function, as shown in Example 3–2.
Example
3–2 JavaScript Event Handler
function sayHello(event)
{
alert("Hello, world!")
}
When the button is clicked, because there
is a client version of the component, the
AdfAction client event is invoked. Because a clientListener tag is configured to
listen for the AdfAction event, it causes the sayHello function to execute. For more
information about client-side events
3.3
Adding JavaScript to a Page
You can either add inline JavaScript directly to a
page or you can import JavaScript
libraries into a page. When you import libraries, you
reduce the page content size, the
libraries can be shared across pages, and they can be
cached by the browser. You
should import JavaScript libraries whenever possible.
Use inline JavaScript only for
cases where a small, page-specific script is needed.
Tip:
including
JavaScript only in the pages that need it
will result in
better performance because those pages that do not need
it will not have
to load it, as they would if the JavaScript were
included in a
template. However, if you find that most of your pages
use the same
JavaScript code, you may want to consider including the
script or the tag
to import the library in a template.
Note,
however, that if a
JavaScript code library becomes too big, you
should consider
splitting it into meaningful pieces and include only
the pieces needed
by the page (and not in a template). This approach
will provide
improved performance, because the browser cache will
be used and the
HTML content of the page will be smaller
3.3.1
How to Use Inline JavaScript
Create and use inline JavaScript in the
same way you would in any JSF application.
Once the JavaScript is on the page, use a clientListener tag to invoke it.
To use inline JavaScript:
1. Add the MyFaces Trinidad
tag library to the root element of the page by adding
the code shown in bold in Example 3–3.
Example
3–3 MyFaces Trinidad Tag Library on a Page
<jsp:root
xmlns:jsp="http://java.sun.com/JSP/Page"version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
xmlns:trh="http://myfaces.apache.org/trinidad/html">
2. Create the JavaScript on
the page.
For example, the sayHello function shown in Example 3–2 might be included in
a JSF page as shown in Example 3–4.
Example
3–4 Inline JavaScript
<af:resource>
function sayHello()
{
alert("Hello, world!")
}
</af:resource>
3. In the Structure window,
right-click the component that will invoke the JavaScript,
and choose Insert inside component > ADF Faces > Client Listener.
4. In the Insert Client
Listener dialog, in the Method field, enter the JavaScript
function name. In the Type field, select the event
type that should invoke the
function.
3.3.2
How to Import JavaScript Libraries
Use the af:resource tag to
access a JavaScript library from a page. This tag should
appear inside the document tag’s metaContainer facet.
To access a JavaScript
library from a page:
1. Below the document tag, add the code shown in bold in Example 3–5 and replace
/mySourceDirectory with the relative path to the directory that holds the
JavaScript library.
Example
3–5 Accessing a JavaScript Library
<af:document>
<f:facet
name="metaContainer">
<af:resource source="/mySourceDirectory"/>
</facet>
<af:form></af:form>
</af:document>
2. In the Structure window,
right-click the component that will invoke the JavaScript,
and choose Insert inside component > ADF Faces > Client Listener.
3. In the Insert Client
Listener dialog, in the Method field, enter the fully qualified
name of the function. For example, if the sayHello function was in the
MyScripts library,
you would enter MyScripts.sayHello. In the Type field,
select the event type that should invoke
the function.
3.3.3
What You May Need to Know About Accessing Client Event Sources
Often when your JavaScript needs to access
a client, it is within the context of a listener
and must access the event’s source
component. Use the getSource() method to get
the client component. Example 3–6 shows the sayHello function accessing the source
client component in order to display its
name.
Example
3–6 Accessing a Client Event Source
function sayHello(actionEvent)
{
var component=actionEvent.getSource();
//Get the ID for the component
var id=component.getId
alert("Hello from "+id);
}
For more information about accessing
client event sources, see Section 5.3, "Using
JavaScript for ADF Faces Client
Events." For
more information about accessing
client-side properties, see Section 3.7,
"Accessing Component Properties on the Client."
For a complete description of how client
events are handled at runtime, see
Section 5.3.6, "What Happens at
Runtime: How Client-Side Events Work."
3.4
Instantiating Client-Side Components
The RCF does not make any guarantees about
which components will have
corresponding client-side component
instances by default. You will usually interact
with client-side components by registering
a clientListener handler. When a
component has a registered clientListener handler, it will
automatically have
client-side representation. If you have to
access another component on the client, then
explicitly configure that component to be
available on the client by setting the
clientComponent attribute
to true.
When you set the clientComponent attribute to true, the framework creates an
instance of an AdfUIComponent class for the component.
This class provides the API
that you can work with on the client side
and also provides basic property accessor
methods (for example, getProperty() and setProperty()), event listener
registration, and event delivery-related
APIs. The framework also provides
renderer-specific subclasses (for example,
AdfRichOutputText) which expose
property-specific accessor methods (for
example, getText() and setText()). These
accessor methods are simply wrappers
around the AdfUIComponent class’s
getProperty() and setProperty() methods and are provided
for coding
convenience.
For example, suppose you have an outputText component on the page that will get
its value (and therefore the text to
display) from the sayHello function. That function
must be able to access the outputText component in order to set its value. For
this to
work, there must be a client-side version
of the outputText component. Example 3–7
shows the JSF page code. Note that the outputText component has an id value and
the clientComponent attribute
is set to true. Also, note there is no value in the
example, because that value will be set by
the JavaScript.
Example
3–7 Adding a Component
<af:commandButton text="Say Hello">
<af:clientListener method="sayHello"
type="action"/>
</af:commandButton>
<af:outputText
id="greeting" value="" clientComponent="true">
Because the outputText
component will now
have client-side representation, the
JavaScript will be able to locate and work with it.
3.5
Locating a Client Component on a Page
When you need to find a client component
that is not the source of an event, you can
use the AdfUIComponent.findComponent(expr) method. This method is similar
to the JSF UIComponent.findComponent() method, which searches for and returns
the UIComponent object
with an ID that matches the specified search expression. The
AdfUIComponent.findComponent(expr) method simply works on the client
instead of the server.
Example 3–8 shows the sayHello function finding the outputText component
using the component’s ID.
Example
3–8 Finding a Client Component Using findComponent()
function sayHello(actionEvent)
{
var component=actionEvent.getSource();
//Find the client component for the "greeting" af:outputText
var greetingComponent=component.findComponent("greeting");
//Set the value for the outputText component
greetingComponent.setValue("Hello World")
}
Instead of using the AdfUIComponent.findComponent(expr) method, you can
use the AdfPage.PAGE.findComponentByAbsoluteId(absolute expr)
method when you know the absolute
identifier for the component, but you don't have
a component instance to call AdfUIComponent.findComponent(expr) on.
AdfPage.PAGE is a
global object that provides a static reference to the page's context
object. However, if the component you are
finding is within a naming container, then
you must use AdfUIComponent.findComponent. For more information, see
Section 3.5.1, "What You May
Need to Know About Finding Components in Naming
Containers."
Note:
There is also a confusingly named
AdfPage.PAGE.findComponent(clientId)
method, however
this function uses
implementation-specific identifiers that can change
between releases and should not be used
by page authors.
3.5.1
What You May Need to Know About Finding Components in Naming Containers
If the component you need to find is within a
component that is a naming container
(such as pageTemplate, subform, table, and tree), then instead of using the
AdfPage.PAGE.findComponentByAbsoluteId(absolute
expr) method, use
the AdfUIComponent.findComponent(expr)
method. The
expression can be
either absolute or relative.
Tip:
You can determine
whether or not a component is a naming
container by
reviewing the component tag documentation. The tag
documentation
states whether a component is a naming container
Absolute expressions use the fully qualified JSF
client ID (meaning, prefixed with the
IDs of all NamingContainer
components that
contain the component) with a leading
NamingContainer.SEPARATOR_CHAR
character, for
example:
":"
+ (namingContainersToJumpUp * ":") + some ending portion of the
clientIdOfComponentToFind
For example, to find a table whose ID is t1 that is within a panel collection component
whose ID is pc1 contained in a region whose ID is r1 on page that uses the
myTemplate
template, you
might use the following:
:myTemplate:r1:pc1:t1
Alternatively, if both the components (the one doing
the search and the one being
searched for) share the same NamingContainer component somewhere in the
hierarchy, you can use a relative path to perform a
search relative to the component
doing the search. A relative path has multiple leading
NamingContainer.SEPARATOR_CHAR
characters, for
example:
":"
+ clientIdOfComponentToFind
In the preceding example, if the component doing the
searching is also in the same
region as the table, you might use the following:
::somePanelCollection:someTable
Tip:
Think of a naming
container as a folder and the clientId as a
file path. In
terms of folders and files, you use two sequential periods
and a slash (../) to move up in the hierarchy to another folder.
This is the same
thing that the multiple colon (:) characters do in the
findComponent() expression. A single leading colon (:) means
that the file path
is absolute from the root of the file structure. If there
are multiple
leading colon (:) characters at the beginning of the
expression, then
the first one is ignored and the others are counted,
one set of periods
and a slash (../) per colon (:) character.
When deciding whether to use an absolute or relative
path, keep the following in
mind:
■ If you know that the component you are
trying to find will always be in the same
naming container, then use an absolute path.
■ If you know that the component
performing the search and the component you
are trying to find will always be in the same relative
location, then use a relative
path.
There are no getChildren()
or getFacet() functions on the client. Instead, the
AdfUIComponent.visitChildren()
function is
provided to visit all children
components or facets (that is all descendents). See
the ADF Faces JavaScript
documentation for more information.
3.6
Determining the User’s Current Location
ADF Faces provides JavaScript APIs that
return the current contextual page
information, in response to an event. The AdfPage.prototype.getViewId()
function returns the identifier for the
currently displayed view. This ID is set when
either a full page render or a partial
page navigation occurs. The
AdfPage.prototype.getComponentsByType(componentType) function
returns an array of component instances
that match the given component type.
For example, say your application contains
a page with tabs, and each tab is made up
of a number of regions. Each region could
contain other nested regions as well. You
can use the APIs to return a String
identifier that is a combination of the viewId of the
entire page and the viewIds of the fragments displayed in each of the
regions
currently rendered on the page, as shown
in Example
3–9.
3.6.1
How to Determine the User’s Current Location
In order to retrieve the viewID property
of the region component on the client, the
user activity monitoring feature needs to
be enabled by setting a parameter in the
web.xml file.
You then create JavaScript code that builds a String representation of the
viewIds that
make up the current page.
To determine a context
identifier:
1. Double-click the web.xml file.
2. In the source editor, set
the oracle.adf.view.faces.context.ENABLE_
ADF_EXECUTION_CONTEXT_PROVIDER to true.
This parameter notifies ADF Faces that the
ExecutionContextProvider
service provider is enabled. This service
monitors and aggregates user activity
information for the client-initiated
requests.
3. Set oracle.adf.view.rich.automation.ENABLED to true.
This parameter ensures that component IDs
are set for all components. For more
information, see Section A.2.3.10,
"Test Automation."
4. Create the JavaScript to
build the context identifier (for more information about
adding JavaScript, see Section 3.3,
"Adding JavaScript to a Page").
Example 3–9 shows JavaScript used to
get the current view ID for a region.
Example 3–9
JavaScript to Retrieve viewIds
/**
*
Returns a String identifier comprising the page viewId and viewIds
*
of the fragments displayed in each of the displayed regions
*/
TestLibrary.prototype.getCurrentPageInfo
= function()
{
var
pageIdentifier = null;
var
page = AdfPage.PAGE;
if
(page)
{
//
get the viewId of the page
var
viewId = page.getViewId();
//
get all region components currently displayed on the page
var
regionComponents = page.getComponentsByType("oracle.adf.RichRegion");
var
regionViewIds = new Array();
for
(var index = 0; index < regionComponents.length; index++)
//ToDo
3.7
Accessing Component Properties on the Client
3.7.1
How to Set Property Values on the Client
3.7.2
How to Unsecure the disabled Property
3.9
Understanding Rendering and Visibility