ADF's client behaviors is a wonderful feature. It allows to add custom javascript code to any component it is attached to (refer to
list of default ADF behaviors if you don't know what they can do). Unfortunately, they are not in public API, but you can use ADF's internal classes (at your own risk...).
Below I will describe how to create your own client behavior in 11.1.1 release. I will focus on how to create appropriate classes and files and not the actual javascript code been added.
1. Create a tag handler class, that extends
oracle.adfinternal.view.faces.taglib.behaviors.BehaviorTag:
package components.behaviors;
import javax.faces.component.UIComponent;
import oracle.adfinternal.view.faces.taglib.behaviors.BehaviorTag;
public class CustomBehavior extends BehaviorTag {
public CustomBehavior() {
super();
}
protected String getBehavior(UIComponent uIComponent) {
return null;
}
}
Method
getBehavior must return a piece of javascript code that calls client constructor (we will implement it later).
2. If you want your behavior to accept some attributes, add fields of type
ValueExpression and setters for them:
package components.behaviors;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import oracle.adfinternal.view.faces.taglib.behaviors.BehaviorTag;
public class CustomBehavior extends BehaviorTag {
private ValueExpression value;
public CustomBehavior() {
super();
}
protected String getBehavior(UIComponent uIComponent) {
return null;
}
public void setValue(ValueExpression value) {
this.value = value;
}
}
3. Now create a JSF tag in yout taglib, that points to our tag handler class. Add all needed attributes:
<tag>
<name>customBehavior</name>
<tag-class>components.behaviors.CustomBehavior</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<deferred-value/>
</attribute>
</tag>
4. Now we should create a javascript object, that will define our custom behavior.
Create a *.js file (in the same package "components.behaviors" or any other). You can name it anything you want, but it's better to name it the same as containing object. It is also suggested, that javascript object name has a prefix denoting your application (because these names are global in javascript). For example, all ADF objects' names start with "Adf":
AdfDomUtils,
AdfUIComponent, etc.
Let's name our object
CaCustomBehavior (I picked prefix 'Ca'), create file
src/component/behaviors/CaCustomBehavior.js. For now let's leave it empty and create an ADF js-feature.
5. Open
META-INF/adf-js-features.xml file in the project that holds your taglib. (create one if it does not exist). Add a "feature" tag to the "features" element. Like this:
<?xml version="1.0" encoding="windows-1251" ?>
<features xmlns="http://xmlns.oracle.com/adf/faces/feature">
<feature>
<feature-name>CaCustomBehavior</feature-name>
<feature-class>components/behaviors/CaCustomBehavior.js</feature-class>
</feature>
</features>
feature-name preferably must correspond to your javascript object's constructor (that we are yet to create).
6. Let's tell ADF to load the js-feature we created, when our behavior is being rendered. To do this we must override method
getFeatureDependency() in tag-handler class
CustomBehavior to return the name of the js-feature we just created:
@Override
protected String getFeatureDependency() {
return "CaCustomBehavior";
}
7. Let's add some basic code in CaCustomBehavior.js:
CaCustomBehavior.InitClass = function(){
// initialize class (e.g. create constants)
}
// create subclass
AdfObject.createSubclass(CaCustomBehavior, AdfClientBehavior);
// constructor
function CaCustomBehavior(value) {
this.Init(value);
}
// object initialization
CaCustomBehavior.prototype.Init = function(value){
AcHighlightBehavior.superclass.Init.call(this);
this._value = value;
}
// initialize with component
CaCustomBehavior.prototype.initialize = function(component){
// TODO: implement behavior logic here
}
Notice that our constructor accepts 1 argument - "value". But, essentially, your behavior's constructor can have any number of arguments. When there is too many of them, you could pass one object with a multiple fields, holding your arguments.
I will not describe every line of upper code here, main function you should notice is
initialize(component). This function is called every time a component appears on the page. This is, actually, the place to add your custom behavior. For example, you can register event listeners on a component. You can set it to something like
alert(this._value) to test that your behavior is loaded. I suggest to do it before actually writing client-side code.
8. And, finally, the last part. Define in your tag-handler how to create a client-side behavior object by implement
getBehavior method. It must return a
call to javascript object's constructor with "new", and
without a semicolon(or you will get a javascript exception). Something like this:
new CaCustomBehavior('arg').
In our
getBehavior we will also calculate
ValueExpression value:
protected String getBehavior(UIComponent uIComponent) {
ELContext elContext = FacesContext.getCurrentInstance().getELContext();
String val = value == null ? "" : ComponentUtils.resolveString(value.getValue(elContext));
return "new CuCustomBehavior('" + val + "')";
}
And that's it. You can do a lot of things with a client behaviors, once you get accustomed with them, there are really useful, and also provide easy re-usability. (instead of javascript resources and clientListeners). Some things I've done with behaviors: hints inside inputText, custom autosuggest (maybe I will write post with this examples, especially if someone asks =) ) and many more...
Please leave your comments or questions. Say if this was useful to you.