sâmbătă, 27 iunie 2015

[OmniFaces utilities 2.0] Create an ajax behavior which should invoke an ajax listener method expression based on the given EL expression


[OmniFaces utilities] The createAjaxBehavior() method creates an ajax behavior which should invoke an ajax listener method expression based on the given EL expression. The target method must take an AjaxBehaviorEvent as argument. Note that this is essentially the programmatic equivalent of <f:ajax>. So if you intented to create for example a <p:ajax> programmatically, then don't use this method. Check also the UIComponentBase#addClientBehavior(String, ClientBehavior) whereby the string argument represents the client event name, such as action, valueChange, click, blur, etc.

Method:
Usage:

Let's suppose that we have the following simple form:

<h:form>
 ...
 <h:commandButton value="Save"/>
</h:form> 

Further, we want to programmatically add an AJAX behavior (like <f:ajax/>) to the above button, and for this we can write a tag handler that exploits the Components#createAjaxBehavior(). Below is listed only the apply() method (notice the pointed listener, #{commandBean.save}):

@Override
public void apply(FaceletContext ctx, UIComponent parent) throws IOException {

 // avoid the following code to be executed multiple time in the same request
 if (!ComponentHandler.isNew(parent)) { 
     return;
 }

 // obviously, if the parent is not a ClientBehaviorHolder then is not capable to deal with an AJAX behavior
 if (!(parent instanceof ClientBehaviorHolder)) {
     return;
 }

 AjaxBehavior ajaxBehavior = Components.createAjaxBehavior("#{commandBean.save}");
 ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) parent;
 clientBehaviorHolder.addClientBehavior("action", ajaxBehavior);       
}

Or, in order to avoid the risk of adding twice the same AJAX behavior:

AjaxBehavior ajaxBehavior = Components.createAjaxBehavior("#{commandBean.save}");
ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) parent;
List<ClientBehavior> behaviors = clientBehaviorHolder.getClientBehaviors().get("action");
// Guard against adding ourselves twice
if (behaviors == null || !behaviors.contains(ajaxBehavior)) {
    clientBehaviorHolder.addClientBehavior("action", ajaxBehavior);
}

Further, the form will become:

<h:form>
 <h:outputScript name="jsf.js" library="javax.faces" target="head"/>           
 <h:commandButton value="Save"> 
  <t:simpleAjax/>
 </h:commandButton>
</h:form>

Notice that we have explicitly added the jsf.js resource. By default, when JSF finds an AJAX behavior in the current view it will "install" this resource automatically (e.g. in Mojarra, via com.sun.faces.facelets.tag.jsf.core.AjaxHandler#installAjaxResourceIfNecessary()). But, notice that this is true from AJAX behavior explicitly defined in view, not programmatically. Depending on context, the jsf.js can be added via @ResourceDependency (not the case here, but when it is use: @ResourceDependency(library="javax.faces", name="jsf.js", target="head")), or via <h:outputScript>). Of course, you can have a helper method also (maybe inspired from Mojarra source code).

Finally, the listener (#{commandBean.save}) is defined in CommandBean, as:

public void save(AjaxBehaviorEvent evt) {
 LOG.log(Level.INFO, "---------------------Saving---------------------- / {0}", evt);
}

Note If you need to go deeper and understand how Components#createAjaxBehavior()is implemented, then you can check the com.sun.faces.facelets.tag.jsf.core.AjaxHandler methods, especially, AjaxHandler#applyAttachedObject()and AjaxHandler#createAjaxBehavior().

The complete code of this example is on GitHub.

Niciun comentariu:

Trimiteți un comentariu