3 Using ADF Faces Architecture

This chapter outlines the major features of the ADF Faces 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 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.

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

  • ADF Faces additions to the lifecycle: The ADF Faces framework extends the JSF lifecycle, providing additional functionality, including a client-side value lifecycle. For more information, see Chapter 4, "Using the JSF Lifecycle with ADF Faces."

  • Event handling: ADF Faces adheres to standard JSF event handling techniques. In addition, the RCF provides AJAX-based rich postbacks (called partial page rendering), as well as a client-side event model. For more information, see Chapter 5, "Handling Events."

  • Conversion and validation: ADF Faces input components have built-in capabilities to both convert and validate user entries. You can also create your own custom converters and validators. For more information, see Chapter 6, "Validating and Converting Input."

  • Partial page rendering: Partial page rendering (PPR) allows small areas of a page to be refreshed without the need to redraw the entire page. Many ADF Faces components have built-in PPR functionality. In addition, you can declaratively configure PPR so that an action on one component causes a rerender of another. For more information, see Chapter 7, "Rerendering Partial Page Content."

  • Geometry management: ADF Faces provides a number of layout components, many of which support geometry management by automatically stretching their contents to take up available space. For more information, see Chapter 8, "Organizing Content on Web Pages."

  • Messaging and help: The RCF provides the ability to display tooltips, messages, and help for input components, as well as the ability to display global messages for the application. The help framework allows you to create messages that can be reused throughout the application.You create a help provider using a Java class, a managed bean, an XLIFF file, or a standard properties file, or you can link to an external HTML-based help system. For more information, see Chapter 17, "Displaying Tips, Messages, and Help."

  • Hierarchical menu model: ADF Faces provides navigation components that render items such as tabs and breadcrumbs for navigating hierarchical pages. The RCF provides an XML-based menu model that, in conjunction with a metadata file, contains all the information for generating the appropriate number of hierarchical levels on each page, and the navigation items that belong to each level. For more information, see Chapter 18, "Working with Navigation Components."

  • Reusable components: The RCF provides three reusable building blocks that can be used by multiple pages in your application: page fragments that allow you to create a part of a page (for example an address input form); page templates that can provide a consistent look and feel throughout your application that can be updated with changes automatically propagating to all pages using the template; and declarative components that are composite components that developers can reuse, ensuring consistent behavior throughout the application. For more information, see Chapter 19, "Creating and Reusing Fragments, Page Templates, and Components."

  • Applying skins: The RCF allows you to create your own look and feel by creating skins used by the ADF Faces components to change their appearance. For more information, see Chapter 20, "Customizing the Appearance Using Styles and Skins."

  • Internationalization and localization: You can configure your JSF page or application to use different locales so that it displays the correct language based on the language setting of a user's browser. For more information, see Chapter 21, "Internationalizing and Localizing Pages."

  • Accessibility: ADF Faces components have built-in accessibility support for user agents, for example a web browser rendering to nonvisual media such as a screen reader or magnifier. Accessibility support also includes access keys that allow users to access components and links using only the keyboard, and audit rules that provide directions to create accessible images, tables, frames, forms, error messages, and popup dialogs using accessible HTML markup. For more information, see Chapter 22, "Developing Accessible ADF Faces Pages."

  • User-driven personalization: Many ADF Faces components, such as the panelSplitter, allow users to change the display of the component at runtime. By default, these changes live only as long as the page request. However, you can configure your application so that the changes can be persisted through the length of the user's session. For more information, see Chapter 34, "Allowing User Customization on JSF Pages."

  • Drag and drop capabilities: The RCF allows the user to move (cut and paste), copy (copy and paste), or link (copy and paste as a link) data from one location to another. When the drop is completed, the component accepting the drop rerenders using partial page rendering. For more information, see Chapter 35, "Adding Drag and Drop Functionality."

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(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, 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:

Inline JavaScript can increase response payload size, will never be cached in browser, and can block browser rendering. Instead of using inline JavaScript, consider putting all scripts in JavaScript libraries.

If you must use inline JavaScript, include it only in the pages that need it. However, if you find that most of your pages use the same JavaScript code, you may want to consider including the script or import the library in a template.

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 placing it 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>
    

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

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 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 (as described in Section 3.3.3, "What You May Need to Know About Accessing Client Event Sources"), you can search for a component using its absolute ID or by doing a search relative to another component.

Absolute IDs are the fully qualified JSF client ID. When a component is in a naming container, the ID is 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, the absolute ID for 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 might be:

myTemplate:r1:pc1:t1

If both the components (the one doing the search and the one being searched for) share the same NamingContainer component, then you only need to use the component's ID. For example, if you were searching for a table whose ID is t1 and is in the same naming container as the input text component doing the search, you would simply use:

t1

If the component you are searching for is in a different naming container somewhere in the hierarchy, you can use a relative path to perform a search relative to the component doing the search. You use a separator character (:) to "step out" of the current naming container and into the parent naming container. For example, to find a component in the parent naming container you would use:

":" + 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:

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.

When deciding whether to use an absolute ID 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 ID.

  • 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.

3.5.1 How to Locate a Client Component Using its Absolute ID

The AdfPage.PAGE object grants access to all components on a page, either by their component ID or the component locator (used for collection components like table that stamp their children).

The following functions on the AdfPage.PAGE can be used to look up components on a page by their absolute ID:

  • findComponentByAbsoluteId: Use for most components.

    The absolute component Id is built from the component ID and the IDs of the naming containers that are located between the document root and the target component on the page:

    (namingContainersToJumpUp * ":") + some ending portion of the clientIdOfComponentToFind
    

    For example, the absolute ID for 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 might be:

    myTemplate:r1:pc1:t1
    

    This function is the client equivalent of the server method UIViewRoot.findComponent() passing a scopeId or search expression. The difference between finding client and server components is that unless your code specifically creates a client component by setting its clientComponent attribute to true or by using a clientListener, a client component may not exist. Even if the framework happens to create a client component for its own purposes, that client component may not be fully capable. Therefore, you must always create your own client component.

    Additionally, on the server, iterating components such as table or iterator stamp out a single instance of each component contained within them. On the client, an instance is created for each iteration. Therefore, the IDs will change each time the component iterates. Because of this, you cannot use findComponentByAbsoluteID to find components inside iterative and collection components.

  • findComponentByAbsoluteLocator: Use for iterative (i.e. collection) components like the table, or in automated tests.

    The locator Id contains a absolute stamped index that finds the stamped component instance. For example, the absolute locator for an inputText component in a table cell whose ID is it1 might be:

    pc1:t1[absolute stamped index].it1
    
  • findComponent: Use when the absolute ID of the component may change, or when there is a possibility that duplicate component IDs may exist on a page, for example when the component to be found is in a region.

    The AdfPage.PAGE.findComponent function searches a component by its client id, as opposed to its absolute ID. This is the fasted way to retrieving a component instance. The client id is generated into the HTML page output and runtime and is different from the component id defined at design time. Because the client id is not stable, and may even change when a page is rerun, you should never hard code the client id value in a JavaScript. Instead, the client id should be read at runtime from the server-side component and dynamically added to the JavaScript, for example by using the af:clientAttribute tag on the component that raises the JavaScript event.

    For more information about using findComponent, see Section 3.5.3, "How to Locate a Client Component When You Don't Know the Absolute ID."

Tip:

Remember to set the clientID attribute to true on the component that needs to be referenced.

3.5.2 How to Locate a Client Component Relative to Another Component

The following functions can be used to search for components relative to another component:

  • findComponent: Use for most components.

    The findComponent method searches a component starting from a given component instance. The component id to look up must be unique within the scope of the closest ancestor naming container.

    For example, say you have a naming container hierarchy where nc1 is the parent to nc2. You have a button (b1) that resides in nc1 and needs to search for a component (c2) that is located in nc2. The relative search Id to use would be nc2:c2.

    Leading colons in the search Id allow the search to move from the current naming container into the parent container. For example, starting a component search from a button b1 in nc2 and prefixing the component search id with two colons (::c2) searches for the component c2 in the parent naming container nc1. Each additional colon moves up one more parent container in the hierarchy, so that :::c2 would search for the component in the parent naming container of nc1, which also could be the document root. A leading single colon searches the component directly from the document root.

    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.
  • findComponentByAbsoluteLocator: Use for stamped (i.e. collection) components like the table.

    This function is exactly the same as the one used on the AdfPage.PAGE object when searching by absolute ID. It requires the absolute locator Id provided in the search. Though the function starts the search relative from a component, it internally calls out to AdfPage.PAGE.findComponentByAbsoluteLocator.

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.5.3 How to Locate a Client Component When You Don't Know the Absolute ID

There may be cases when you won't know the component's absolute ID until runtime. You can dynamically look up the ID from a managed bean. On the bean, you use the server-side getClientID to access the ID. You set the ID on the client component using a clientAttribute tag and you use the client-side AdfPage.PAGE.findComponentByAbsoluteId to access the ID from the JavaScript.

Before You Begin:

You need to create a backing bean for the page that will be used to keep track of the component ID. Bind the component to be referenced to the backing bean. Example 3-8 shows code that might be used to bind a commandButton.

Example 3-8 Backing Bean Code to Hold Component ID

private ComponentReference clientButton;
  public void setClientButton(RichCommandButton clientButton){
    this.clientButton = ComponentReference.newUIComponentReference(clientButton);
  }
 
  public RichCommandButton getClientButton(){
    if (clientButton != null){
     return (RichCommandButton)clientButton.getComponent();
    }
    else{
     return null;
    }
  }

To access a client component without knowing its absolute ID:

  1. On the component to be referenced from the JavaScript, set clientComponent to true.

  2. Create a new function in the same backing bean that calls the getClientId() function on the Java reference to the component. This function returns the full ID, including the client ID and the path defined by any templates, regions or other naming containers. Example 3-9 shows a function for a commandButton.

    Example 3-9 Function to Return the full ID for a commandButton

    public String getClientButtonClientId(){
        FacesContext ctx = FacesContext.getCurrentInstance();
        return this.getClientButton().getClientId(ctx);
      }
    
  3. Add a clientListener tag to the component whose event will invoke the Javascript. Also add a clientAttribute tag to make the ID available to the JavaScript function. The clientAttribute tag allows you to add attributes to a client component that aren't normally accessible (for more information, see Section 3.8, "Using Bonus Attributes for Client-Side Components"). Example 3-10 shows a second button that needs to reference the "b1" button.

    Example 3-10 clientListener and clientAttribute Tags Used to Access a Component ID

    <af:button text="Invoke Client Operation" id="b2">
      <af:clientListener method="doSomeClientOp" type="action"/>
      <af:clientAttribute name="b1ClientId" 
          value="#{backingBeanScope.examplePage.clientButtonClientId}"/>
    </af:button>
    
  4. Create the JavaScript function to access the client ID using the AdfPage.PAGE.findComponentByAbsoluteId(absolute expr) method.

    Example 3-11 Function to Access the Client ID

    function doSomeClientOp(actionEvent){
      var actionComp = actionEvent.getSource();
      var b1ClientId = actionComp.getProperty("b1ClientId"); 
      var b1Comp = AdfPage.PAGE.findComponent(b1ClientId);
      ...
    } 
    

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-12.

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-12 shows JavaScript used to get the current view ID for a region.

    Example 3-12 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.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++)
        {
          var regionComp = regionComponents[index]);
          if (regionComp)
          {
            regionViewIds.push(regionComp.getProperty("viewId"));
          }
        }
        // construct page identifier
        if (viewId != null && regionViewIds.length > 0)
          contextId = viewId.concat(regionViewIds.toString());
      }
      return contextId;
    }
    

3.7 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 client properties in ADF Faces use the getXyz function naming convention including boolean properties. The isXyz naming convention for boolean properties 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.

As noted in Section 1.2.2, "ADF Faces Architectural Features," most property values that are set on the client result in automatic synchronization with the server (although some complex Java objects are not sent to the client at all). There are however, two 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, "Using the Immediate Attribute"). Consequently, the immediate property is a secured property.

Attempts to set any other secured property from JavaScript will fail. For more information, see Section 3.7.2, "How to Unsecure the disabled Property." Table 3-1 shows the secure properties on the client components.

Table 3-1 Secure Client Properties

Component Secure Property

AdfRichChooseColor

colorData

AdfRichComboboxListOfValue

disabled

readOnly

AdfRichCommandButton

disabled

readOnly

blocking

AdfRichCommandImageLink

blocking

disabled

partialSubmit

AdfRichCommandLink

readOnly

AdfRichDialog

dialogListener

AdfRichDocument

failedConnectionText

AdfRichInputColor

disabled

readOnly

colorData

AdfRichInputDate

disabled

readOnly

valuePassThru

AdfRichInputFile

disabled

readOnly

AdfRichInputListOfValues

disabled

readOnly

AdfRichInputNumberSlider

disabled

readOnly

AdfRichInputNumberSplinBox

disabled

readOnly

maximum

minimum

stepSize

AdfRichInputRangeSlider

disabled

readOnly

AdfRichInputText

disabled

readOnly

secret

AdfRichPopUp

launchPopupListener

model

returnPopupListener

returnPopupDataListener

createPopupId

AdfRichUIQuery

conjunctionReadOnly

model

queryListener

queryOperationListener

AdfRichSelectBooleanCheckbox

disabled

readOnly

AdfRichSelectBooleanRadio

disabled

readOnly

AdfRichSelectManyCheckbox

disabled

readOnly

valuePassThru

AdfRichSelectManyChoice

disabled

readOnly

valuePassThru

AdfRichSelectManyListBox

disabled

readOnly

valuePassThru

AdfRichSelectManyShuttle

disabled

readOnly

valuePassThru

AdfRichSelectOneChoice

disabled

readOnly

valuePassThru

AdfRichSelectOneListBox

disabled

readOnly

valuePassThru

AdfRichSelectOneRadio

disabled

readOnly

valuePassThru

AdfRichSelectOrderShuttle

disabled

readOnly

valuePassThru

AdfRichUITable

filterModel

AdfRichTextEditor

disabled

readOnly

AdfUIChart

chartDrillDownListener

AdfUIColumn

sortProperty

AdfUICommand

actionExpression

returnListener

launchListener

immediate

AdfUIComponentRef

componentType

AdfUIEditableValueBase

immediate

valid

required

localValueSet

submittedValue

requiredMessageDetail

AdfUIMessage.js

for

AdfUINavigationLevel

level

AdfUINavigationTree

rowDisclosureListener

startLevel

immediate

AdfUIPage

rowDisclosureListener

immediate

AdfUIPoll

immediate

pollListener

AdfUIProgress

immediate

AdfUISelectBoolean

selected

AdfUISelectInput

actionExpression

returnListener

AdfUISelectRange

immediate

rangeChangeListener

AdfUIShowDetailBase

immediate

disclosureListener

AdfUISingleStep

selectedStep

maxStep

AdfUISubform

default

AdfUITableBase

rowDisclosureListener

selectionListener

immediate

sortListener

rangeChangeListener

showAll

AdfUITreeBase

immediate

rowDisclosureListener

selectionListener

focusRowKey

focusListener

AdfUITreeTable

rowsByDepth

rangeChangeListener

AdfUIValueBase

converter


ADF Faces does allow you to configure the disabled property so that it can be made unsecure. This can be useful when you need to use JavaScript to enable and disable buttons. When you set the unsecure property to true, the disabled property (and only the disabled property) will be made unsecure.

Disconnected properties are those that can be set on the client, but that do not propagate back to the server. These properties have 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, such properties 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.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.7.2 How to Unsecure the disabled Property

You use the unsecured property to set the disabled property to be unsecure. You need to manually add this property and the value of disabled to the code for the component whose disabled property should be unsecure. For example, the code for a button whose disabled property should be unsecured would be:

<af:commandButton text="commandButton 1" id="cb1" unsecure="disabled"/>

Once you set the unsecure attribute to disabled, a malicious JavaScript could change the disabled attribute unwittingly. For example, say you have an expense approval page, and on that page, you want certain managers to be able to only approve invoices that are under $200. For this reason, you want the approval button to be disabled unless the current user is allowed to approve the invoice.

If you did not set the unsecured attribute to disabled, the approval button would remain disabled until a round-trip to the server occurs, where logic determines if the current user can approve the expense. But because you want the button to display correctly as the page loads the expense, say you set the unsecure attribute to disabled. Now you can use JavaScript on the client to determine if the button should be disabled. But now, any JavaScript (including malicious JavaScript that you have no control over) can do the same thing.

To avoid this issue, you must ensure that your application still performs the same logic as if the round-trip to the server had happened. In the expense report approval screen, you might have JavaScript that checks that the amount is under $200, but you still need to have the action for the approval button perform the logic on the server. Adding the logic to the server ensures that the disabled attribute does not get changed when it should not.

Similarly, if you allow your application to be modified at runtime, and you allow users to potentially edit the unsecure and/or the disabled attributes, you must ensure that your application still performs the same logic as if the round-trip to the server had occurred.

3.7.3 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 (as long as the value is different). Also, setting a property may cause the component to rerender itself.

3.8 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, using the clientAttribute tag results in the bonus attribute being 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, booleans, 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 Data."

Performance Tip:

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.8.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 the Structure window, select the component to which you would like to add a bonus attribute.

  2. In the Component Palette, from the Operations panel, drag and drop a Client Attribute as a child to the component.

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

3.8.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.9 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, the 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 instead 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-13 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 is entered.

Example 3-13 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-14 shows how you might achieve the same functionality as shown in Example 3-13, but in this example, the visible attribute is used to determine which component is displayed (the rendered attribute is true by default, it does not need to be explicitly set).

Example 3-14 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-15 shows the page code for JavaScript that handles the visiblity of the components.

Example 3-15 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.9.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-15 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-16 shows the full page code used to toggle visibility with JavaScript.

Example 3-16 JavaScript Toggles Visibility

<f:view>
<af:resource>
  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)
      }
      
  }
</af:resource>
<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.9.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 a 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.