ADF Faces client behavior tags provide declarative solutions to common client operations that you would otherwise have to write using JavaScript, and register on components as client listeners.
Using tags makes behavior much more reusable. All a page developer has to do is drag-and-drop the client behavior tag on the appropriate component and not worry about writing JavaScript and attaching the appropriate client listeners. ADF Faces 11gR1 has a number of client behavior tags and a couple more were added in version 11.1.2. Some examples are the af:showPopupBehavior to disclose a popup or the af:scrollComponentIntoViewBehavior to scroll the page.
Wouldn't it be great if we can create our own client behavior tags for things that ADF doesn't support out of the box? There are a number of blogs out there that describe something like that, such as placeholder watermarks by Duncan Mills and setting the initial focus component by Donatas Valys. Unfortunately those blogs describe how to do this with facelets which requires JDeveloper 11.1.2 We needed a solution for JDeveloper 11.1.1 and thus JSP tags.
Update: A follow-up post is available that explains how to add support for attributes to your custom client behavior.
Update: A follow-up post is available that explains how to add support for attributes to your custom client behavior.
There are a number of things we need to get this to work:
- JSP tag handler java class
- Tag library descriptor (.tld) file to register the JSP tag
- javascript file with the object that implements the client behavior
- adf-js-features.xml to register with the ADF Faces JavaScript Library Partitioning framework to dynamically load the javascript when needed
- Deploy this as a library so we can consume it in any project we want
- Optionally enhance the tag and behavior to accept attributes to customize the behavior
Read on as we discuss these step by step based on a overly simplified example of showing a javascript alert when clicking a command component like a button. Remember this blog post is about showing how to wire everything up not about an actual useful client behavior. I will cover some real world examples in future posts.
JSP Tag Handler
First let's start with writing the JSP tag handler. This is a java class that represents the actual tag in a JSP document. It is responsible for receiving any attribute values from the JSP document and normally instantiates the JSF component. In this case we don't really have a server side JSF component as we are implementing client behavior. The easiest way to accomplish this is to extend the class that the out-of-the-box ADF Faces behaviors also use. The class to extend is oracle.adfinternal.view.faces.taglib.behaviors.BehaviorTag. Unfortunately this class is in the oracle.adfinternal package which means it is not part of the public javadoc and it might change in a future version as it is not an official public API. The easiest way to understand what is going on is to get a copy of the ADF source code from Oracle support and have a look for yourself.
The javascript class that is returned by getBehavior will be implemented in a javascript file below, while the behavior returned by getFeatureDependency is registered in a xml file below.
Pro Tip: As an alternative to getBehavior(UIComponent) you can also implement getBehaviorExpression(UIComponent) that doesn't return javascript itself but a ValueExpression that should evaluate to the javascript snippet. The benefit of this approach is that the ValueExpression is executed with the correct ELContext. An example would be a behavior in an ADF Table or Iterator where you need access to the iterator or row variable.
package com.redheap.clientbehavior; import javax.faces.component.UIComponent; /** * Implementation of showAlertBehavior JSP tag. * @author Wilfred van der Deijl, www.redheap.com */ public class ShowAlertBehaviorTag extends oracle.adfinternal.view.faces.taglib.behaviors.BehaviorTag { /** * Returns the snippet of javascript code that needs to be executed * on the client to construct the client behavior object. * @param component JSF component this behavior tag is attached to * @return snippet of javascript that instantiates an object */ protected String getBehavior(UIComponent component) { return "new RedHeapShowAlertBehavior()"; } /** * Returns the name of a JavaScript library feature that provides * the client-side implementation of this listener/behavior. * This feature will be added to the set of features required for * this page/request, ensuring that corresponding JavaScript * library partition is available. * @return a javascript library feature name */ @Override protected String getFeatureDependency() { return "RedHeapShowAlertBehavior"; } }
The javascript class that is returned by getBehavior will be implemented in a javascript file below, while the behavior returned by getFeatureDependency is registered in a xml file below.
Pro Tip: As an alternative to getBehavior(UIComponent) you can also implement getBehaviorExpression(UIComponent) that doesn't return javascript itself but a ValueExpression that should evaluate to the javascript snippet. The benefit of this approach is that the ValueExpression is executed with the correct ELContext. An example would be a behavior in an ADF Table or Iterator where you need access to the iterator or row variable.
Tag Library Descriptor
Next thing we need is a tag libary descriptor file so the JSP parser understands our <redheap:showAlertBehavior> tag and links it to the java class we just created. Use the New Gallery (from the File menu) to add a JSP Tag Library:
Most of the time you will be making these behaviors with reuse in mind, so we opt to create a deployable tag library that is intended to be used in a library that we include with all of our projects. If you want behavior tags just for your one project you could also create a project based taglib:
Finally we have to supply the information for the taglib itself. Use sensible values in your situation and remember the taglib URI doesn't have to be a real URL it just has to be a globally unique identifier, much like a XML namespace:
Finish the wizard with all default values and you should end up in the editor for your new taglib file which should be located in the src/META-INF directory. Add a single tag to the taglib xml file and point it to the tag class we just created:
One thing to note is that the short-name of the taglib itself will be used as the prefix of the tags in JSP documents (the redheap in <redheap:showAlertBehavior>) while the display-name will show up in the dropdown of component families in the component palette in JDeveloper.
new gallery for JSP taglib |
Most of the time you will be making these behaviors with reuse in mind, so we opt to create a deployable tag library that is intended to be used in a library that we include with all of our projects. If you want behavior tags just for your one project you could also create a project based taglib:
deployable taglib |
Finally we have to supply the information for the taglib itself. Use sensible values in your situation and remember the taglib URI doesn't have to be a real URL it just has to be a globally unique identifier, much like a XML namespace:
supplying taglib information |
Finish the wizard with all default values and you should end up in the editor for your new taglib file which should be located in the src/META-INF directory. Add a single tag to the taglib xml file and point it to the tag class we just created:
<?xml version = '1.0' encoding = 'UTF-8'?> <taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"> <description>Describe the purpose of your library here</description> <display-name>redheap</display-name> <tlib-version>1.0</tlib-version> <short-name>redheap</short-name> <uri>http://redheap.com/taglib</uri> <tag> <description>Describe your component here</description> <name>showAlertBehavior</name> <tag-class>com.redheap.clientbehavior.ShowAlertBehaviorTag</tag-class> <body-content>JSP</body-content> </tag> </taglib>
One thing to note is that the short-name of the taglib itself will be used as the prefix of the tags in JSP documents (the redheap in <redheap:showAlertBehavior>) while the display-name will show up in the dropdown of component families in the component palette in JDeveloper.
Javascript Client Behavior Implementation
The tag handler java class returned a snippet of javascript that instantiates a RedHeapShowAlertBehavior. Next thing we need to do is to actually implement this class in a javascript file.
This file doesn't have to be in the publicly accessible public_html directory but can be safely tucked away in the java src directory as it will be loaded by the ADF framework from the classpath and not directly by the client browser (more on that later). I opted to put the javascript file in the same package as the tag handler:
new gallery for javascript file |
This file doesn't have to be in the publicly accessible public_html directory but can be safely tucked away in the java src directory as it will be loaded by the ADF framework from the classpath and not directly by the client browser (more on that later). I opted to put the javascript file in the same package as the tag handler:
put the javascript file in the src directory |
All that is left is to actually implement the javascript:
/** * javascript constructor that invokes the Init function */ function RedHeapShowAlertBehavior() { this.Init(); } // register as a subclass of AdfClientBehavior AdfObject.createSubclass(RedHeapShowAlertBehavior, AdfClientBehavior); /** * initialize this behavior * @override */ RedHeapShowAlertBehavior.prototype.Init = function() { // be sure to invoke initialization by superclass RedHeapShowAlertBehavior.superclass.Init.call(this); } /** * as part of the AdfClientBehavior behavior contract, initialize is * called when the component is created to give the behavior a chance * to register event listeners. * @param {AdfUIComponent} component */ RedHeapShowAlertBehavior.prototype.initialize = function(component) { AdfAssert.assertPrototype(component, AdfUIComponent); component.addEventListener(AdfActionEvent.ACTION_EVENT_TYPE, this._handleAction, this); } /** * handle invoking a command component with this behavior. * @param {AdfUIInputEvent} event */ RedHeapShowAlertBehavior.prototype._handleAction = function(event) { AdfAssert.assertPrototype(event, AdfUIInputEvent); alert('Yes we managed to trigger the client behavior'); event.cancel(); // cancel normal event processing }
We are registering this behavior class as a subclass of AdfClientBehavior just as the default ADF behaviors. This ensures the initialize method (lines 28-32) is called with a reference to the JSF component we are adding behavior to. All we do there is register appropriate event listeners on the component, in this instance for the action (click) event. There are many types of events you can choose from but all of them are subclasses of AdfComponentEvent. In this example we listen to the action event which is defined in AdfActionEvent while keyboard events live in AdfUIInputEvent.
Whenever the user clicks the JSF command component our handler (lines 39-43) will be invoked. In this example we only show a javascript alert and cancel the event itself to prevent normal processing. This prevents the default handling of a button to submit the page to the server.
Register Javascript Feature
Registering an ADF javascript feature has the benefit of ADF taking care of dynamically loading the necessary javascript files when needed and as an added bonus it will take care of appropriate http headers to influence caching of these resources. Start by adding a XML file to your project:
Be sure to name the file adf-js-features.xml and it has to be located in the src/META-INF directory otherwise the framework cannot find it:
Register your javascript feature in this file. The feature-name has to match the name that was returned by getFeatureDependency() in your tag handler class while feature-class is the path to your javascript file within the classpath:
new gallery for XML document |
Be sure to name the file adf-js-features.xml and it has to be located in the src/META-INF directory otherwise the framework cannot find it:
creating src/META-INF/adf-js-features.xml |
Register your javascript feature in this file. The feature-name has to match the name that was returned by getFeatureDependency() in your tag handler class while feature-class is the path to your javascript file within the classpath:
<?xml version="1.0" encoding="UTF-8" ?> <features xmlns="http://xmlns.oracle.com/adf/faces/feature"> <feature> <feature-name>RedHeapShowAlertBehavior</feature-name> <feature-class>com/redheap/clientbehavior/RedHeapShowAlertBehavior.js</feature-class> </feature> </features>
Library Deployment And Usage
All that is left is to bundle this all up as a library and consume it in any project you like. First thing you have to do is set the compiler options in your project properties to also include javascript files. Double click the project in JDeveloper and add ;.js to the file types to copy to the output directory:
If you added the tag library through the wizards JDeveloper already added a deployment profile to the project. This profile is fine for these simple cases although you could also setup an ADF Library that includes both this behavior and some reusable ADF components. For now, let's just use the default taglib deployment profile:
Now open the project where you want to consume this library. Double click the project to open the project properties. Navigate to the JSP Tag Libraries and add a new one to the project:
Browse to the JAR file we just created. When returning to the JSP Tag Library dialog check the checkbox to execute the tags in the visual editor. For behaviors there isn't really a need to run these in the visual editor in JDeveloper but we might add JSF or ADF components at a later stage that actually render something in the visual editor:
Now finally build a simple page an in the visual editor select the new component family in the component palette:
Drag and drop the behavior on the component where you want to use it:
Finally run the page and click the button to see the behavior in action as it shows a javascript alert:
source code. And even though it is not the most fancy application you can also see it live on Oracle Cloud.
setup project to copy javascript to output directory |
If you added the tag library through the wizards JDeveloper already added a deployment profile to the project. This profile is fine for these simple cases although you could also setup an ADF Library that includes both this behavior and some reusable ADF components. For now, let's just use the default taglib deployment profile:
deploy using default taglib deployment profile |
Now open the project where you want to consume this library. Double click the project to open the project properties. Navigate to the JSP Tag Libraries and add a new one to the project:
adding JSP taglib to the project |
add JAR library to the project |
Browse to the JAR file we just created. When returning to the JSP Tag Library dialog check the checkbox to execute the tags in the visual editor. For behaviors there isn't really a need to run these in the visual editor in JDeveloper but we might add JSF or ADF components at a later stage that actually render something in the visual editor:
execute tags in visual editor |
Now finally build a simple page an in the visual editor select the new component family in the component palette:
select component family |
Drag and drop the behavior on the component where you want to use it:
drag-and-drop behavior on component |
Finally run the page and click the button to see the behavior in action as it shows a javascript alert:
running sample |
Future Posts
This post has already grown too big, so I'll try to wrap up and keep the other stuff for future posts. Things I still have to cover is how to pass parameters to the behavior (like customizing the message in this alert), how to complement the sample with a facelets tag library so it will also work in version 11.1.2 when using facelets and one real world sample where we use keyboard listener to insert a comma as decimal mark when the user is using the dot on the numerical keyboard.Sample Application
As always you can download the full sample application or browse the subversion repository to look at thesource code. And even though it is not the most fancy application you can also see it live on Oracle Cloud.
Thanks. This gives a good jumpstart for understanding ClientBehavior.
ReplyDeleteApart from adding it a JSP Tag Library, I had to add it a Facelets Tag Library to get this working. I was using Oracle ADF 12.1.2.