ADF Faces Rich Client - JavaScript Programming Nuggets
Last update: 21 - July- 2008
ADF Faces Rich Client in JDeveloper 11 is a JavaServer Faces UI component set that helps developers to build Ajax applications the smart way without having to dig too much into JavaScript client programming matters.
However, a good framework leaves you the option and so does ADF Faces Rich Client components. You can use the client side framework to actively use JavaScript to raise events or manipulate content. Its the framework itself that provides you hooks and methods for almost everything you can do on the server to do it on the client as well. Of course there are fine lines of best practices not to cross.
ADF Faces RC JavaScript client architecture
JavaScript is not Java! Surprised ? Though JavaScript has Java in its name, it has nothing to do with Java, and in fact doesn't even from the same company. For many years JavaScript had been banned from enterprise client application development because of alleged security risks.
Ajax however made it possible to bring JavaScript back into spotlight, without the security concerns being lifted. As it seems the web developer community is more feature driven than security paranoid and so they happily waved away the security concerns. I don't go into this discussion ,but in fact I agree that you can relax on this matter. Not on security in general, but on the possibility of a widened attack surfaced through the use of JavaScript in Ajax, compared to traditional web applications. Stay on your security watch towers, but don't mind Ajax.
In Ajax, JavaScript is used for what only a few new it was possible and the majority wasn't even interested: Object oriented programming! Most of know JavaScript from alerts that popup in HTML forms or dependent list boxes that work so nicely on online registration forms. However, there is more to master in Ajax than this.
[... TBC ... ]
Adding JavaScript on a page
Before JavaScript can do its little wonders, you need to get it onto the page first. For this the following markup can be added below the af:document tag
-
<![CDATA[
-
<script>
-
/* if called from a client listener */
-
function name(event) { }
-
/* ordinary JavaScript function */
-
function name1 () { }
-
...
-
</script> ]]>
Best practices is to add JavaScript into the metaContainer facet of the af:document element
-
<f:view>
-
<af:document>
-
<f:facet name="metaContainer">
-
<af:group>
-
<![CDATA[<script>function name(_event) {
-
...
-
}
-
</script> ]]>
-
</af:group>
-
</f:facet>
-
...
-
</af:document>
-
</f:view>
Basic Usecases
Basic usecases explain how to access a specific information from the ADF Faces Rich client framework. JavaScript savvy developers will be able to use the published the code snippets in their own ADF Faces RC applications.
Detecting the client browser agent
ADF Faces Rich Client framework allows developers to determine the version of the client agent used with an application. The agents that can be specified are
- AdfAgent.js
- AdfSafariAgent.js
- AdfOperaAgent.js
- AdfGecko18Agent.js
- AdfGeckoAgent.js
- AdfIEAgent.js
- AdfIE6Agent.js
-
<f:verbatim>
-
<![CDATA[
-
<script>
-
function whichAgent(event) {
-
agent = AdfAgent.getAgent();
-
if(agent.constructor == AdfGecko18Agent){
-
alert("Mozilla");
-
}
-
if(agent.constructor == AdfIEAgent){
-
alert("IE");
-
}
-
}
-
</script> ]]>
-
</f:verbatim>
Getting the x/y position of a mouseclick on a component
If you know how to get the x/y coordinates of a mouseclick in JavaScript then you might be surprised to see that your favorite technique fails in ADF Faces RC. The reason for this is that if the event comes from a client listener in ADF Faces, the event object is one of ADF Faces RC
-
<f:view>
-
<af:document>
-
<f:verbatim>
-
<![CDATA[
-
<script>
-
function handleMouseCoordinates(event) {
-
alert("x: "+event.getPageX()+" y: "+event.getPageY());
-
}
-
</script>
-
]]>
-
</f:verbatim>
-
<af:form>
-
<af:commandButton text="My Click Button" icon="/images/jdev_cup.gif">
-
<af:clientListener method="handleMouseCoordinates" type="click"/>
-
</af:commandButton>
-
</af:form>
-
</af:document>
-
</f:view>
Getting the keycode of the pressed key
The key event in ADF Faces is a subclass of AdfDomAdfAdfUIInputEvent and provides a cross browser functionality to obtain the pressed key e.g in an input field.
To call a JavaScript function - e.g. whichkey - you need to add an af:clientListener in the jspx page for the input component, e.g. af:textField
-
<af:inputText label="Autosuggest">
-
<af:clientListener method="whichkey" type="keyUp"/>
-
</af:inputText>
This invokes the following JavaScript added to or linked from the page
-
<![CDATA[
-
<script>
-
function whichkey(event) {
-
alert(event.getKeyCode());
-
}
-
</script>
-
]]>
If the executing agent is Firefox, then the event class is "AdfGeckoAdfAdfUIInputEvent", if the agent is IE then the event class is "AdfIEUIInputEvent".
Accessing the content of a selected table row
Tables in ADF Faces Rich client components are stamped for performance reasons. This means that they are built on the server and don't use a client object for the table rows, columns and cells they contain. If a developer needs to access the content of the selected table cell on the client, or the content of selected table cells in a multi select table, then there is an option to do so, which includes the declarative creation of such objects. As a note of caution, keep in mind that any client object you create for an ADF Faces application will have its impact on performance when rendering the page. In Ajax, the penalty for two many client objects is download size.
When you create the ADF Faces table - e.g. by dragging and dropping a ViewObject from the ADF data control palette onto the JSF page - then you not only decide whether or not the table should be read-only, but also, if the table rows should become selectable. The latter is what usually you want. Note that if you missed setting the select option in the ADF binding editor, you can always define this in the component properties.
Making a table selectable doesn't notify any JavaScript on the client, which is why an additional component, the af:clientListener is required. The af:clientListener component dispatches the table selection to a JavaScript method whenever a pre-defined event - selection in this case - happens.
-
<af:table value="#{bindings.DepartmentsView1.collectionModel}" var="row"
-
rows="#{bindings.DepartmentsView1.rangeSize}"
-
first="#{bindings.DepartmentsView1.rangeStart}"
-
emptyText="#{bindings.DepartmentsView1.viewable ? 'No rows yet.' : 'Access Denied.'}"
-
fetchSize="#{bindings.DepartmentsView1.rangeSize}"
-
selectedRowKeys="#{bindings.DepartmentsView1.collectionModel.selectedRow}"
-
selectionListener="#{bindings.DepartmentsView1.collectionModel.makeCurrent}"
-
rowSelection="single">
-
<af:clientListener method="showDepartmentsName" type="selection"/>
-
...
The client listener in the above example calls a JavaScript method "showDepartmentsName" and passes in the event object, which is of AdfSelectionEvent type. The AdfSelectionEvent class hierarchy, in case you are interested, is
-
AdfPhasedEvent
-
|
-
|-AdfRowKeySetChangeEvent
-
|
-
|- AdfSelectionEvent
The AdfPhaseEvent's getSource() method, gives us a handle to the table component - as the source of the event. The table component has a method findComponent("name",rowKey) that allows us to search for a specific client component that it contains.
The client component in a read only table is those of a outputTextField, which represents a table cell. The client components, as mentioned before, don't exist by default and are created by setting the "clientComponent" property to true.
-
<af:column sortProperty="DepartmentName" sortable="false">
-
<af:outputText value="#{row.DepartmentName}"
-
clientComponent="true"
-
id="dname"/>
-
</af:column>
The two lines - defining clientComponent = true and id=" dname" - make it possible that the department name is rendered in a client object, which is accessible by the name "dname".
To access the content of the selected dname table cell, we can now call
var cellhandler = _theTable.findComponent("dname",firstRowKey);
The full "showDepartmentsName" JavaScript is
-
<f:verbatim>
-
<