This chapter includes the following sections:
ADF Faces exposes a complete client-side architecture that wraps the browser specific Document Object Model (DOM) API and renders ADF Faces components as client-side JavaScript objects for internal framework use, for use by ADF Faces component developers and for use by ADF Faces application developers.
ADF Faces extends the JavaServer Faces architecture, adding a client-side framework on top of the standard server-centric model. The majority of ADF Faces components are rendered in HTML that is generated on the server-side for a request. In addition, ADF Faces allows component implementations to extend their reach to the client using a client-side component and event model.
The ADF Faces framework already contains much of the functionality for which you would ordinarily need to use JavaScript. In many cases, you can achieve rich component functionality declaratively, without the use of JavaScript. However, there may be times when you do need to add your own JavaScript, for example custom processing in response to a client-side event. In these cases, you can use the client-side framework.
The JavaScript class that you will interact with most is AdfUIComponent
and its subclasses. An instance of this class is the client-side representation of a server-side component. You can think of a client-side component as a simple property container with support for event handling. Client-side components primarily exist to add behavior to the page by exposing an API contract for both application developers as well as for the framework itself. It is this contract that allows, among other things, toggling the enabled state of a button on the client.
Each client component has a set of properties (key/value pairs) and a list of listeners for each supported event type. All ADF Faces JavaScript classes are prefixed with Adf
to avoid naming conflicts with other JavaScript libraries. For example, RichButton
has AdfButton
, RichDocument
has AdfRichDocument
, and so on.
In the client-side JavaScript layer, client components exist mostly to provide an API contract for the framework and for developers. Because client components exist only to store state and provide an API, they have no direct interaction with the document object model (DOM) whatsoever. All DOM interaction goes through an intermediary called the peer. Peers interact with the DOM generated by the Java renderer and handle updating that state and responding to user interactions.
Peers have a number of other responsibilities, including:
DOM initialization and cleanup
DOM event handling
Geometry management
Partial page response handling
Child visibility change handling
This separation isolates the component and application developer from changes in the DOM implementation of the component and also isolates the need for the application to know whether a component is implemented in HTML DOM at all (for example the Flash components).
In JSF, as in most component-based frameworks, an intrinsic property of the component model is that components can be nested to form a hierarchy, typically known as the component tree. This simply means that parent components keep track of their children, making it possible to walk over the component tree to find all descendents of any given component. While the full component tree exists on the server, the ADF Faces client-side component tree is sparsely populated.
For performance optimization, 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. You don't need to understand the client framework as except for exceptional cases, you use most of the architectural features declaratively, without having to create any code.
For example, because the framework does not create client components for every server-side component, there may be cases where you need a client version of a component instance. Instantiating Client-Side Components, explains how to do this declaratively. You use the Properties window in JDeveloper to set properties that determine whether a component should be rendered at all, or simply be made not visible, as described in Understanding Rendering and Visibility.
Note:
It is also possible for JavaScript components to be present that do not correspond to any existing server-side component. For example, some ADF Faces components have client-side behavior that requires popup content. These components may create AdfRichPopup
JavaScript components, even though no server-side Java RichPopup
component may exist.
Other functionality may require you to use the ADF Faces JavaScript API. For example, Locating a Client Component on a Page, explains how to use the API to locate a specific client-side component, and Accessing Component Properties on the Client, documents how to access specific properties.
A common issue with JavaScript-heavy frameworks is determining how best to deliver a large JavaScript code base to the client. If all the code is in a single JavaScript library, there will be a long download time, while splitting the JavaScript into too many libraries will result in a large number of roundtrips. To help mitigate this issue, ADF Faces aggregates its JavaScript code into partitions. A JavaScript library partition contains code for components and/or features that are commonly used together. See JavaScript Library Partitioning.
ADF Faces exposes JavaScript APIs that application developers use to implement client-side development use cases. To use JavaScript on an ADF Faces view, you can either add JavaScript code to the page source or reference it in an external library file.
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:
IInline 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.
Create the JavaScript on the page and then use a clientListener
tag to invoke it.
Before you begin:
It may be helpful to have an understanding of adding JavaScript to a page. For information, see Adding JavaScript to a Page.
To use inline JavaScript:
Use the af:resource
tag to access a JavaScript library from a page. This tag should appear inside the document
tag's metaContainer
facet.
Before you begin:
It may be helpful to have an understanding of adding JavaScript to a page. For information, see Adding JavaScript to a Page.
To access a JavaScript library from a page:
Often when your JavaScript needs to access a client component, 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. The following example shows the sayHello
function accessing the source client component in order to display its name.
function sayHello(actionEvent) { var component=actionEvent.getSource(); //Get the ID for the component var id=component.getId(); alert("Hello from "+id); }
For information about accessing client event sources, see Using JavaScript for ADF Faces Client Events. For information about accessing client-side properties, see Accessing Component Properties on the Client. For a complete description of how client events are handled at runtime, see What Happens at Runtime: How Client-Side Events Work.
The ADF Faces framework automatically handles JavaScript object instantiation. This supports interacting with the client components and allows developers to manually configure a component to have a client side instance.
By default, the framework does not make any guarantees about which components will have corresponding client-side component instances. To interact with a component on the client, you will usually register a clientListener
handler. When a component has a registered clientListener
handler, it will automatically have client-side representation. You can also explicitly configure a component to be available on the client by setting the clientComponent
attribute to true
.
You can manually configure a component to have a client side instance using the clientComponent
attribute.
Performance Tip:
Only set clientComponent
to true
if you plan on interacting with the component programmatically on the client.
Note:
When the framework creates a client component for its own uses, that client component may only contain information the framework needs at that time. For example, not all of the attributes may be available.
Before you begin:
It may be helpful to have an understanding of client-side instances. See Instantiating Client-Side Components.
To configure a component for a client-side instance:
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, as shown in the following 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.
<af:button text="Say Hello"> <af:clientListener method="sayHello" type="action"/> </af:button> <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.
Note:
The ADF Faces framework may create client components for its own purposes, even when there are no client listeners or when the clientComponent
attribute is not set to true. However, these client components may not be fully functional.
ADF Faces client component objects can be created by adding the af:clientListener
tag, which is a declarative way to register a client-side listener script to be executed when a specific event type occurs. You can apply the client listener to programmatically created components or to components created for its own use, and also to control navigating away from a JSF page.
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 declaratively using the af:clientListener
tag.
You use the af:clientListener
tag to call corresponding Javascript in response to a client event. For example, suppose you have a button that, in response to a click, should display a “Hello World" alert. You need to first create the JavaScript function that will respond to the event by displaying the alert. You then add the client listener to the component that will invoke that function.
Before you begin:
It may be helpful to have an understanding of client event processing. See Listening for Client Events.
To listen for a client event:
Tip:
Because the button has a registered client listener, the framework will automatically create a client version of the component.
When the button is clicked, because there is a client version of the component, the Adf
Action
client event is invoked. Because a clientListener
tag is configured to listen for the AdfAction
event, it causes the sayHello
function to execute. For information about client-side events, see Using JavaScript for ADF Faces Client Events.
When the user attempts to close the browser that displays a JSF page in an ADF Faces application, you can control navigation away from a page by displaying a native browser confirmation dialog to confirm the navigation. To handle this type of event, the af:document
component supports calling an ADF client listener of type beforeunload
and ensures that the ADF Faces application will not result in an undefined order of execution that can cause issues in some browsers. Specifically, do not handle this type of navigation event using the browser native beforeunload
handler like window.addEventListener("beforeunload", function(e) {...}
.
With support for the onbeforeunload
event in Oracle ADF, you can define a client event listener to call an event handler on the af:document
component. Your ADF event handler will be called before leaving or reloading the current page occurs, and the user may cancel the exit entirely within the execution of the ADF Faces framework. If the user continues to navigate away from the page, the browser will raise the window.unload
event and the ADF Faces application will terminate.
For example, your client listener can handle the ADF beforeunload
event in a manner similar to how the handler for a browser native beforeunload
handler operates and should return a string that prompts the user to cancel the exit.
... <af:document> <af:resource type="javascript"> function myBeforeUnload(beforeunloadEvent) { return "Are you sure that you want to leave?" } </af:resource> <af:clientListener type="beforeunload" method="myBeforeUnload"/>
When your ADF beforeunload
handler is called, if it returns a value other than undefined, then a browser controlled warning dialog will be displayed asking whether to actually navigate away from the current page. Depending on the browser, the returned string may be displayed as part of the warning dialog. The warning message may also be set by calling the setWarningMessage
method on the ADF beforeunload
object passed to the handler.
ADF Faces extends the JavaServer Faces architecture, adding a client-side framework on top of the standard server-centric model. You can get or set component attribute properties on the client using JavaScript.
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 in some JavaScript execution environments, 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.
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 Link
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 information about using the immediate
property, see Using the Immediate Attribute). Consequently, the immediate
property is a secured property. Attempts to set secured property from JavaScript will fail. See How to Unsecure the disabled Property.
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. See Secure Client Properties for a list of the ADF Faces secure client properties.
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.
Secure properties are those that cannot be set on the client, preventing a security hole. Table 4-1 shows the secure properties on the client components.
Table 4-1 Secure Client Properties
Component | Secure Property |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The ADF Faces framework provides setXYZ
convenience functions that call through to the underlying ADFUIComponent.setProperty
function, passing the appropriate property name (for more information, see the JavaScript API Reference for Oracle ADF Faces). The following code shows how you might use the setProperty
function to set the backgroundcolor
property on an inputText
component to red when the value changes.
<af:form> <af:resource type="javascript"> function color(event) { var inputComponent = event.getSource(); inputComponent.setproperty("inlineStyle", "background-color:Red"); } </af:resource> <af:outputText id="it" label="label"> <af:clientListener method="color" type="valueChange"/> </af:inputText> </af:form>
By using these functions, you can change the value of a property, and as long as it is not a disconnected property or a secure property, the value will also be changed on the server.
Calling the setProperty()
function on the client sets the property to the new value, and synchronously fires a PropertyChangeEvent
event with the old and new values (as long as the value is different). Also, setting a property may cause the component to rerender itself.
By default, the disabled property is a secure property. That is, JavaScript cannot be used to set it on the client. However, you can 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:button text="commandButton 1" id="cb1" unsecure="disabled"/>
When the disabled
attribute was secured, the application could count on the disabled
attribute to ensure that an action event was never delivered when it shouldn't be. Therefore, when you do unsecure the disabled attribute, the server can no longer count on the disabled attribute being correct and so you must to perform the equivalent check using an actionListeners.
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. Without unsecuring the disabled
attribute, he approval button would remain disabled until a round-trip to the server occurs, where logic determines if the current user can approve the expense. This means that upon rendering, the button may not display correctly for the current user. To have the button to display correctly as the page loads, you need to set the unsecure
attribute to disabled
and then 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 the malicious JavaScript, use the actionListener
on the button to 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. In the expense approval example, you might have JavaScript that checks that the amount is under $200, and also use the actionListener
on the button to recheck that the current manager has the appropriate spending authority before performing the approval.
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.
There may be cases when you do not want the value of the property to always be delivered and synchronized to the server. For example, say you have inputText
components in a form, and as soon as a user changes a value in one of the components, you want the changed indicator to display. To do this, you might use JavaScript to set the changed
attribute to true
on the client component when the valueChangeEvent
event is delivered. Say also, you do not want the changed indicator to display once the user submits the page, because at that time, the values are saved.
Say you use JavaScript to set the changed attribute to true when the valueChangeEvent
is delivered, like this:
<af:form> <af:resource type="javascript"> function changed(event) { var inputComponent = event.getSource(); inputComponent.setChanged(true); } </af:resource> <af:inputText id="it" label="label"> <af:clientListener method="changed" type="valueChange"/> </af:inputText> <af:button text="Submit"/> </af:form>
Using this example, the value of the changed
attribute, which is true
, will also be sent to the server, because all the properties on the component are normally synchronized to the server. So the changed indicator will continue to display.
To make it so the indicator does not display when the values are saved to the server, you might use one of the following alternatives:
Move the logic from the client to the server, using an event listener. Use this alternative when there is an event being delivered to the server, such as the valueChangeEvent
event, as shown here:
<af:form> <af:inputText label="label" autoSubmit="true" changed="#{test.changed}" valueChangeListener="#{test.valueChange}"/> <af:button text="Submit" /> </af:form>
This example shows the corresponding managed bean code.
import javax.faces.event.ValueChangeEvent; import oracle.adf.view.rich.context.AdfFacesContext; public class TestBean { public TestBean() {} public void valueChange(ValueChangeEvent valueChangeEvent) { setChanged(true); AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); adfFacesContext.addPartialTarget(valueChangeEvent.getComponent()); FacesContext.getCurrentInstance().renderResponse(); } public void setChanged(boolean changed) { _changed = changed; } public boolean isChanged() { return _changed; } private boolean _changed; }
Move the logic to the server, using JavaScript that invokes a custom server event and a serverListener
tag, as shown below. Use this when there is no event being delivered.
<af:form> <af:resource type="javascript"> function changed(event) { var inputComponent = event.getSource(); AdfCustomEvent.queue(inputComponent, "myCustomEvent", null, true); } </af:resource> <af:inputText label="label" changed="#{test2.changed}"> <af:serverListener type="myCustomEvent" method="#{test2.doCustomEvent}"/> <af:clientListener method="changed" type="valueChange"/> </af:inputText> <af:button text="Submit"/> </af:form>
The following example shows the managed bean code.
package test; import javax.faces.context.FacesContext; import oracle.adf.view.rich.context.AdfFacesContext; import oracle.adf.view.rich.render.ClientEvent; public class Test2Bean { public Test2Bean() { } public void doCustomEvent(ClientEvent event) { setChanged(true); AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); adfFacesContext.addPartialTarget(event.getComponent()); FacesContext.getCurrentInstance().renderResponse(); } public void setChanged(boolean changed) { _changed = changed; } public boolean isChanged() { return _changed; } private boolean _changed; }
On the client component, set the changed
attribute to true
, which will propagate to the server, but then use an actionListener
on the command component to set the changed
attribute back to false
. Here is the JSF code:
<af:form> <af:resource type="javascript"> function changed(event) { var inputComponent = event.getSource(); inputComponent.setChanged(true); } </af:resource> <af:inputText binding="#{test3.input}" label="label"> <af:clientListener method="changed" type="valueChange"/> </af:inputText> <af:button text="Submit" actionListener="#{test3.clear}"/> </af:form>
The following sample shows the corresponding managed bean code.
package test; import javax.faces.event.ActionEvent; import oracle.adf.view.rich.component.rich.input.RichInputText; public class Test3Bean { public Test3Bean() { } public void clear(ActionEvent actionEvent) { _input.setChanged(false); } public void setInput(RichInputText input) { _input = input; } public RichInputText getInput() { return _input; } private RichInputText _input; }
In ADF Faces, bonus attributes are used to return values from server to client. You can create bonus attributes to a component using the Components window in JDeveloper.
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 framework 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 information on marshalling, see 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
. See Instantiating Client-Side Components.
You can use the Components window to add a bonus attribute to a component.
Before you begin:
It may be helpful to have an understanding of bonus attributes. See Using Bonus Attributes for Client-Side Components.
To create 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. See Sending Custom Events from the Client to the Server.
You can show (render) and hide an ADF Faces component dynamically on your page, depending on the value of another component. You must set a component's attribute property to true or false to be a part of JSF tree and to generate HTML code for that component.
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 framework 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. However, because visible
simply shows and hides the content in the DOM, it doesn't always provide the same visual changes as using the rendered
would.
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.
The following example 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.
<af:panelGroupLayout layout="horizontal"> <af:inputText label="Input some text" id="input" value="#{myBean.inputValue}"/> <af:button 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.
The next example shows how you might achieve the same functionality as shown above, 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).
<af:panelGroupLayout layout="horizontal"> <af:inputText label="Input some text" id="input" value="#{myBean.inputValue}"/> <af:button 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, as shown in the following JavaScript code.
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) } }
You can create a conditional JavaScript function that can toggle the visible
attribute of components.
Before you begin:
It may be helpful to have an understanding of how components are displayed. See Understanding Rendering and Visibility.
To set visibility:
outputText
component if there is no value; otherwise, the script turns visibility on for the other outputText
component.true
. This creates a client component that will be used by the JavaScript.visible
attribute to false
.This example shows the full page code used to toggle visibility with JavaScript.
Example 4-1 JavaScript for Setting 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:button text="Enter" clientComponent="true"> <af:clientListener method="showText" type="action"/> </af:button> </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>
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 framework 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
.
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.
The following example shows the sayHello
function finding the outputText
component using the component's ID.
function sayHello(actionEvent) { var buttonComponent=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") }
ADF Faces also has the AdfPage.PAGE.findComponentByAbsoluteId(absolute expr)
method. Use this method when you want to hard-code the String for the ID. Use AdfUIComponent.findComponent(expr)
when the client ID is being retrieved from the component.
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.
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 are built as follows:
":" + (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.
Note that if you were to use the AdfPage.findComponentByAbsoluteId()
method, no leading colon is needed as, the path always absolute.
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). Because ADF Faces uses a sparse component tree (that is, client components are created on an as-needed basis, the component that the getParent()
method might return on the client may not be the actual parent on the server (it could be any ancestor). Likewise, the components that appear to be immediate children on the client could be any descendants. See the Java API Reference for Oracle ADF Faces.
In ADF Faces, JavaScript Partitioning feature helps combine individual JavaScript files from ADF components together into larger collections, thus reducing the number of downloads. You can use the default partition provided by ADF Faces or create/configure a JavaScript partition as required by the application.
A common issue with JavaScript-heavy frameworks is determining how best to deliver a large JavaScript code base to the client. On one extreme, bundling all code into a single JavaScript library can result in a long download time. On the other extreme, breaking up JavaScript code into many small JavaScript libraries can result in a large number of roundtrips. Both approaches can result in the end user waiting unnecessarily long for the initial page to load.
To help mitigate this issue, ADF Faces aggregates its JavaScript code into partitions. A JavaScript library partition contains code for components and/or features that are commonly used together. By default, ADF Faces provides a partitioning that is intended to provide a balance between total download size and total number of roundtrips.
One benefit of ADF Faces's library partitioning strategy is that it is configurable. Because different applications make use of different components and features, the default partitioning provided by ADF Faces may not be ideal for all applications. As such, ADF Faces allows the JavaScript library partitioning to be customized on a per-application basis. This partitioning allows application developers to tune the JavaScript library footprint to meet the needs of their application.
ADF Faces groups its components' JavaScript files into JavaScript features. A JavaScript feature is a collection of JavaScript files associated with a logical identifier that describes the feature. For example, the panelStretchLayout
client component is comprised of the following two JavaScript files
oracle/adf/view/js/component/rich/layout/ AdfRichPanelStretchLayout.js
oracle/adfinternal/view/js/laf/dhtml/rich/ AdfDhtmlPanelStretchLayoutPeer.js
These two files are grouped into the AdfRichPanelStretchLayout
feature.
JavaScript features are further grouped into JavaScript partitions. JavaScript partitions allow you to group JavaScript features into larger collections with the goal of influencing the download size and number of round trips. For example, since the panelStretchLayout
component is often used with the panelSplitter
component, the features for these two components are grouped together in the stretch partition, along with the other ADF Faces layout components that can stretch their children. At runtime, when a page is loaded, the framework determines the components used on the page, and then from that, determines which features are needed (feature names are the same as the components' constructor name). Only the partitions that contain those features are downloaded.
Features and partitions are defined using configuration files. ADF Faces ships with a default features and partitions configuration file. You can overwrite the default partitions file by creating your own implementation.
By default, JavaScript partitioning is turned on. Whether or not your application uses JavaScript partitioning is determined by a context parameter in the web.xml
file. See JavaScript Partitioning.
You create a JavaScript partition by creating an adf-js-partitions.xml file
, and then adding entries for the features.
Note:
ADF Faces provides a default adf-js-partitions.xml
file (see The adf-js-partitions.xml File). If you want to change the partition configuration, you need to create your own complete adf-js-partitions.xml
file. At runtime, the framework will search the WEB-INF
directory for that file. If one is not found, it will load the default partition file.
Before you begin:
It may be helpful to have an understanding of JavaScript partitioning works. For information, see JavaScript Library Partitioning.
To create JavaScript partitions:
ADF Faces loads the library partitioning configuration files at application initialization time. First, ADF Faces searches for all adf-js-features.xml
files in the META-INF
directory and loads all that are found (including the ADF Faces default feature configuration file).
For the partition configuration file, ADF Faces looks for a single file named adf-js-partitions.xml
in the WEB-INF
directory. If no such file is found, the ADF Faces default partition configuration is used.
During the render traversal, ADF Faces collects information about which JavaScript features are required by the page. At the end of the traversal, the complete set of JavaScript features required by the (rendered) page contents is known. Once the set of required JavaScript features is known, ADF Faces uses the partition configuration file to map this set of features to the set of required partitions. Given the set of required partitions, the HTML <script>
references to these partitions are rendered just before the end of the HTML document.