Question

I am busy writing my own JSF2 UIComponents and their relevant renderers. All of my UIComponents implements ClientBehaviorHolder. What I don't understand is how to really render ClientBehaviorHolder.

For example, the following code illustrates how ClientBehaviorHolder is rendered in Mojarra.

private static void renderHandler(FacesContext context,
                                  UIComponent component,
                                  Collection<ClientBehaviorContext.Parameter> params,
                                  String handlerName,
                                  Object handlerValue,
                                  String behaviorEventName,
                                  String submitTarget,
                                  boolean needsSubmit,
                                  boolean includeExec)
    throws IOException {

    ResponseWriter writer = context.getResponseWriter();
    String userHandler = getNonEmptyUserHandler(handlerValue);
    List<ClientBehavior> behaviors = getClientBehaviors(component, behaviorEventName);

    // Don't render behavior scripts if component is disabled
    if ((null != behaviors) && 
        (behaviors.size() > 0) && 
         Util.componentIsDisabled(component)) {
        behaviors = null;
    }

    if (params == null) {
        params = Collections.emptyList();
    }
    String handler = null;
    switch (getHandlerType(behaviors, params, userHandler, needsSubmit, includeExec)) {

        case USER_HANDLER_ONLY:
            handler = userHandler;
            break;

        case SINGLE_BEHAVIOR_ONLY:
            handler = getSingleBehaviorHandler(context, 
                                               component,
                                               behaviors.get(0),
                                               params,
                                               behaviorEventName,
                                               submitTarget,
                                               needsSubmit);
            break;

        case SUBMIT_ONLY:
            handler = getSubmitHandler(context, 
                                       component,
                                       params,
                                       submitTarget,
                                       true);
            break;

        case CHAIN:
            handler = getChainedHandler(context,
                                        component,
                                        behaviors,
                                        params,
                                        behaviorEventName,
                                        userHandler,
                                        submitTarget,
                                        needsSubmit);
            break;
        default:
            assert(false);
    }


    writer.writeAttribute(handlerName, handler, null);
}

For submit handlers, Mojarra adds the mojarra.jsfcljs javascript, UIParameters and other scripts. For chain handlers, jsf.util.chain is used.

My question is:

  • How does one determine if we have to render handlers in chain or a single behaviour or user specific handler?
  • mojarra.jsfcljs is only unique to Mojarra. PrimeFaces have their own implementation, so does Apache Tomahawk. Question is: what does mojarra.jsfcljs do and what is its use? This is so that I can write one for my own? Also, where can I find the implementation of mojarra.jsfcljs?
  • What is the specification to render ClientBehaviorHolder?

My sincere thanks in advance.

Was it helpful?

Solution

How does one determine if we have to render handlers in chain or a single behaviour or user specific handler?

Imagine that the enduser (read: the JSF developer who's using your component) programmed:

<your:component onclick="return foo()" />

And you intented to ultimately render for your component's own purpose:

<someHtmlElement onclick="jsf.ajax.request(...); return false;" />

Then you can't just concatenate the enduser's onclick in front of your component's jsf.ajax.request() like so

<someHtmlElement onclick="return foo(); jsf.ajax.request(...); return false;" />

Even if it returned true, your component's jsf.ajax.request won't be invoked at all. You ultimately want to end up something like:

<someHtmlElement onclick="if returnsTrue('return foo();') { jsf.ajax.request(...); } return false;" />

That's exactly what jsf.util.chain() is doing under the covers.


mojarra.jsfcljs is only unique to Mojarra. PrimeFaces have their own implementation, so does Apache Tomahawk. Question is: what does mojarra.jsfcljs do and what is its use? This is so that I can write one for my own? Also, where can I find the implementation of mojarra.jsfcljs?

It's inside the jsf.js file. Easy way to find it is to open a JSF page with <f:ajax> embedded and look in the generated <head> source for the <script> with its URL. This file is by default minified. If you set javax.faces.PROJECT_STAGE context param to Development, then this will be served unminified. The task of the jsfcljs() function is to submit the parent form with the necessary parameters. Here's an extract of relevance coming from Mojarra 2.1.21.

/*
 * This is called by command link and command button.  It provides
 * the form it is nested in, the parameters that need to be
 * added and finally, the target of the action.  This function
 * will delete any parameters added <em>after</em> the form
 * has been submitted to handle DOM caching issues.
 *
 * @param f - the target form
 * @param pvp - associative array of parameter
 *  key/value pairs to be added to the form as hidden input
 *  fields.
 * @param t - the target of the form submission
 */
mojarra.jsfcljs = function jsfcljs(f, pvp, t) {

What is the specification to render ClientBehaviorHolder?

Use ClientBehavior#getScript() to get the autogenerated script. It requires a ClientBehaviorContext argument which can be created using ClientBehaviorContext#createClientBehaviorContext(). It's in turn your responsibility to render it into the appropriate HTML attribute, such as onclick.

FacesContext context = FacesContext.getCurrentInstance();
UIComponent inputOrCommandComponent = ...; // Your component.
String event = "click"; // Just the particular HTML DOM event name you need to listen on.

ClientBehaviorContext clientBehaviorContext = ClientBehaviorContext.createClientBehaviorContext(context, component, event, component.getClientId(context), null);
StringBuilder builder = new StringBuilder();

for (ClientBehavior behavior : component.getClientBehaviors().get(event)) { // Collect all <f:ajax> declarations on the given event.
    builder.append(behavior.getScript(clientBehaviorContext));
    builder.append(';');
}

String script = builder.toString();
// Write it to the desired HTML attribute.

Note that you absolutely don't need to worry about writing JSF implementation specific scripts this way. They will be generated for you.

All with all, ClientBehaviorHolder is just an abstraction of ajax support. It allows developers to nest <f:ajax> in your component. All standard JSF UIInput and UICommand components implement it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top