Skip Headers
Oracle® Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework
11g Release 1 (11.1.1)

Part Number B31973-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

3 Using ADF Faces Architecture

This chapter outlines the major ADF Faces architecture features and also provides information for using different features of the client-side architecture.

This chapter includes the following sections:

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 in to the framework. Many of these features are found in the client side of the architecture. 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 just not visible, as described in Section 3.8, "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.6, "Accessing Component Properties on the Client" documents how to access specific properties.

The following RCF features are more complex, and therefore have full chapters devoted to them:

The remainder of this chapter focuses on working with the client-side framework.

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>

Tip:

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()
  {
    alert("Hello, world!")
  }

When the button is clicked, because there is 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, see Section 5.3, "Using JavaScript for ADF Faces Client 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.

Performance 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 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-5.

    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

    <trh:script>
      function sayHello()
      {
        alert("Hello, world!")
      }
    </trh:script>
    

    Note:

    Do not use the f:verbatim tag in a page or template to specify the JavaScript.
  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 MyFaces Trinidad trh:script 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. Add the MyFaces Trinidad tag library to the root element of the page by adding the code shown in bold in Example 3-5.

    Example 3-5 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. Below the document tag, add the code shown in bold in Example 3-6 and replace /mySourceDirectory with the relative path to the directory that holds the JavaScript library.

    Example 3-6 Accessing a JavaScript Library

    <af:document>
      <f:facet name="metaContainer">
        <trh:script source="/mySourceDirectory"/>
      </facet>
      <af:form></af:form>
    </af:document>
    
  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 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-7 shows the sayHello function accessing the source client component in order to display its name.

Example 3-7 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, see Section 5.3, "Using JavaScript for ADF Faces Client Events". For more information about accessing client-side properties, see Section 3.6, "Accessing Component Properties on the Client". For a complete description of how client events are handled at runtime, see Section 5.3.5, "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.

Performance Tip:

Only set clientComponent to true if you plan on interacting with the component programmatically on the client.

When you set the clientComponent attribute to true, the framework creates an instance of an AdfUIComponent class for the component. This provides the API that you can work with on the client side. The class 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 get 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-8 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-8 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, you can use the AdfUIComponent.findComponent(expr) method. This 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-9 shows the sayHello function finding the outputText component using the component's ID.

Example 3-9 Finding a Client Component Using findComponent()

function sayHello(actionEvent)
{
  var component=actionEvent.getSource();
  
  //Find the client component for the "greeting" af:outputText
  var greetingComponent=buttonComponent.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 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. See the following section for more information on absolute identifiers and 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 if a component is a naming container.

Absolute expressions use the fully qualified JSF client ID (meaning prefixed with the IDs of all of the 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 that is within a panelCollection component contained in a region on page that uses a template, you might use the following:

:someTemplate:someRegion:somePanelCollection:someTable

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 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 Accessing Component Properties on the Client

For each built-in property on a component, convenience accessor methods are available on the component class. For example, you can call the getValue() method on a client component and receive the same value that was used on the server.

Note:

All properties use the getXyz function naming convention including boolean properties. The isXyz naming convention for boolean properties in JavaScript is not used.

Constants are also available for the property names on the class object. For instance, you can use AdfRichDialog.STYLE_CLASS constant instead of using "styleClass".

Note:

In JavaScript, it is more efficient to refer to a constant than to code the string, as the latter requires an object allocation on each invocation.

When a component's property changes, the end result should be that the component's DOM is updated to reflect its new state, in some cases without a roundtrip to the server. The component's role in this process is fairly limited, it simply stores away the new property value and then notifies the peer of the change. The peer contains the logic for updating the DOM to reflect the new component state.

Note:

Not all property changes are handled through the peer on the client side. Some property changes are propagated back to the server and the component is rerendered using (PPR).

3.6.1 How to Set Property Values on the Client

The RCF provides setXYZ convenience functions that provide calls to the AdfUIComponent setProperty() function. The setProperty() function takes the following arguments:

  • Property name (required)

  • New value (required)

3.6.2 What Happens at Runtime: How Client Properties Are Set on the Client

Calling the setProperty() function on the client sets the property to the new value, and synchronously fires a PropertyChangeEvent event with the new values if the value changed. Also, setting a property may cause the component to rerender itself.

3.6.3 What You May Need to Know About Secured and Disconnected Properties

As noted in Section 1.2.2, "ADF Faces Architectural Features", setting most property values on the client acts as if the property were set on the server, and results in automatic synchronization with the server, (although some complex Java objects are not sent to the client at all). There are also two other types of properties that act differently: secured properties and disconnected properties.

Secured properties are those that cannot be set on the client at all. For example, say a malicious client used JavaScript to set the immediate flag on a commandLink component to true. That change would then be propagated to the server, resulting in server-side validation being skipped, causing a possible security hole (for more information about using the immediate property, see Section 4.2.1, "Using the Immediate Attribute"). Consequently, the immediate property is a secured property. Attempts to set a secured property from JavaScript will fail.

Disconnected properties are those that can be set on the client, but do not propagate back to the server. This is used when the property has a lifecycle on the client that is independent of the lifecycle on the server. For example, client form input components (like AdfRichInputText) have a submittedValue property, just as the Java EditableValueHolder components do. However, setting this property does not directly affect the server. In this case, standard form submission techniques handle updating the submitted value on the server.

A property can be both disconnected and secured. In practice, these act like disconnected properties on the client; they can be set on the client, but will not be sent to the server. But they act like secured properties on the server in that they will refuse any client attempts to set them.

3.7 Using Bonus Attributes for Client-Side Components

In some cases you may want to send additional information to the client beyond the built-in properties. This can be accomplished using bonus attributes. Bonus attributes are extra attributes that you can add to a component using the clientAttribute tag. For performance reasons, the only bonus attributes sent to the client are those specified by clientAttribute.

The clientAttribute tag specifies a name/value pair that is added to the server-side component's attribute map. In addition to populating the server-side attribute map, when using the clientAttribute tag, the bonus attribute is also sent to the client, where it can be accessed through the AdfUIComponent.getProperty("bonusAttributeName") method.

The RCF takes care of marshalling the attribute value to the client. The marshalling layer supports marshalling of a range of object types, including strings, boolean, numbers, dates, arrays, maps, and so on. For more information on marshalling, see Section 5.4.3, "What You May Need to Know About Marshalling and Unmarshalling of Data".

Performance Note:

In order to avoid excessive marshalling overhead, use client-side bonus attributes sparingly.

Note:

The clientAttribute tag should be used only for bonus (application-defined) attributes. If you need access to standard component attributes on the client, instead of using the clientAttribute tag, simply set the clientComponent attribute to true. For more information, see Section 3.4, "Instantiating Client-Side Components".

3.7.1 How to Create Bonus Attributes

You can use the Component Palette to add a bonus attribute to a component.

To create bonus attributes:

  1. In JDeveloper, select the component to which you would like to add a bonus attribute.

  2. From the Operations section of the Component Palette, drag and drop a Client Attribute as a child to the component.

  3. In the Property Inspector, set the Name and Value attributes.

3.7.2 What You May Need to Know About Marshalling Bonus Attributes

Although client-side bonus attributes are automatically delivered from the server to the client, the reverse is not true. That is, changing or setting a bonus attribute on the client will have no effect on the server. Only known (nonbonus) attributes are synchronized from the client to the server. If you want to send application-defined data back to the server, you should create a custom event. For more information, see Section 5.4, "Sending Custom Events from the Client to the Server".

3.8 Understanding Rendering and Visibility

All ADF Faces display components have two attributes that relate to whether or not the component is displayed on the page for the user to see: rendered and visible.

The rendered attribute has very strict semantics. When rendered is set to false, there is no way to show a component on the client without a roundtrip to the server. To support dynamically hiding and showing page contents, RCF adds the visible attribute. When set to false, the component's markup is available on the client but the component is not displayed. Therefore calls to the setVisible(true) or setVisible(false) method will, respectively, show and hide the component within the browser (as long as rendered is set to true), whether those calls happen from Java or from JavaScript.

Performance Tip:

You should set the visible attribute to false only when you absolutely need to be able to toggle visibility without a roundtrip to the server, for example in JavaScript. Nonvisible components still go through the component lifecycle, including validation.

If you do not need to toggle visibility only on the client, then you should set the rendered attribute to false. Making a component not rendered (instead of not visible) will improve server performance and client response time because the component will not have client-side representation, and will not go through the component lifecycle.

Example 3-10 shows two outputText components, only one of which is rendered at a time. The first outputText component is rendered when no value has been entered into the inputText component. The second outputText component is rendered when a value has been entered.

Example 3-10 Rendered and Not Rendered Components

<af:panelGroupLayout layout="horizontal">
  <af:inputText label="Input some text" id="input"
                value="#{myBean.inputValue}"/>
  <af:commandButton text="Enter"/>
</af:panelGroupLayout>
<af:panelGroupLayout layout="horizontal">
  <af:outputLabel value="You entered:"/>
  <af:outputText value="No text entered" id="output1"
                 rendered="#{myBean.inputValue==null}"/>
  <af:outputText value="#{myBean.inputValue}"

                 rendered="#{myBean.inputValue !=null}"/></af:panelGroupLayout>

Provided a component is rendered in the client, you can either display or hide the component on the page using the visible property.

Example 3-11 shows how you might achieve the same functionality as shown in Example 3-10, but determines which component is displayed using the visible attribute instead of the rendered attribute (the rendered attribute is true by default, it does not need to be explicitly set).

Example 3-11 Visible and Not Visible Components

<af:panelGroupLayout layout="horizontal">
  <af:inputText label="Input some text" id="input"
                value="#{myBean.inputValue}"/>
  <af:commandButton text="Enter"/>
</af:panelGroupLayout>
<af:panelGroupLayout layout="horizontal">
  <af:outputLabel value="You entered:"/>
  <af:outputText value="No text entered" id="output1"
                 visible="#{myBean.inputValue==null}"/>
  <af:outputText value="#{myBean.inputValue}"
                 visible="#{myBean.inputValue !=null}"/>
</af:panelGroupLayout>

However, because using the rendered attribute instead of the visible attribute improves performance on the server side, you may instead decide to have JavaScript handle the visibility.

Example 3-12 shows the page code for JavaScript that handles the visiblity of the components.

Example 3-12 Using JavaScript to Turn On Visibility

function showText() 
{
    var output1 = AdfUIComponent.findComponent("output1")
    var output2 = AdfUIComponent.findComponent("output2")
    var input = AdfUIComponent.findComponent("input")
    
    if (input.getValue() == "")
    {
      output1.setVisible(true);
    }
    else 
    {
      output2.setVisible(true)
    }
    
 }
   

3.8.1 How to Set Visibility Using Javascript

You can create a conditional JavaScript function that can toggle the visible attribute of components.

To set visibility:

  1. Create the JavaScript that can toggle the visibility. Example 3-12 shows a script that turns visibility on for one outputText component if there is no value, otherwise the script turns visibility on for the other outputText component.

  2. For each component that will be needed in the JavaScript function, expand the Advanced section of the Property Inspector and set the ClientComponent attribute to true. This creates a client component that will be used by the JavaScript.

  3. For the components whose visibility will be toggled, set the visible attribute to false.

Example 3-13 shows the full page code used to toggle visibility with JavaScript.

Example 3-13 JavaScript Toggles Visibility

<f:view>
<f:verbatim>
  <script type="text/javascript" language="javascript">
    function showText() 
    {
        var output1 = AdfUIComponent.findComponent("output1")
        var output2 = AdfUIComponent.findComponent("output2")
        var input = AdfUIComponent.findComponent("input")
        
        if (input.value == "")
        {
        output1.setVisible(true);
        }
        else 
        {
        output2.setVisible(true)
        }
        
    }
    </script>
    </f:verbatim>
  <af:document>
  
    <af:form>
    <!--  <af:panelGroupLayout layout="horizontal"> -->
        <af:inputText label="Input some text" id="input"
                      value="#{myBean.inputValue}" clientComponent="true"
                      immediate="true"/>
        <af:commandButton text="Enter" clientComponent="true">
          <af:clientListener method="showText" type="action"/>
        </af:commandButton>
     <!-- </af:panelGroupLayout> -->
     <!-- <af:panelGroupLayout layout="horizontal"> -->
        <af:outputLabel value="You entered:" clientComponent="false"/>
        <af:outputText value="No text entered" id="output1"
                       visible="false" clientComponent="true"/>
        <af:outputText value="#{myBean.inputValue}" id="output2"
                       visible="false" clientComponent="true"/>
    <!--  </af:panelGroupLayout> -->
    </af:form>
  </af:document>
</f:view>

3.8.2 What You May Need to Know About Visible and the isShowing Function

If the parent of a component has its visible attribute set to false, when the isVisible function is run against a child component whose visible attribute is set to true, it will return true, even though that child is not displayed. For example, say you have panelGroupLayout component that contains an outputText component as a child, and the panelGroupLayout component's visible attribute is set to false while the outputText component's visible attribute is left as the default (true). On the client, neither the panelGroupLayout nor the outputText component will be displayed, but if the isVisible function is run against the outputText component, it will return true.

For this reason, the RCF provides the isShowing() function. This function will return false if the component's visible attribute is set to false, or if any parent of that component has visible set to false.