3

I have to implement a three state checkbox as a composite component with ajax features. This is what I have so far:

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:fn="http://java.sun.com/jsp/jstl/functions"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich">

    <cc:interface>
        <cc:attribute name="id" /><!-- necessary? -->
        <cc:attribute name="value" required="false" />
        <cc:clientBehavior name="change" event="change" targets="value" />
    </cc:interface>
    <cc:implementation>
        <div style="float: left;">
            <h:outputStylesheet library="default" name="css/checkbox.css" target="head" />
            <h:outputScript library="default" name="js/checkbox.js" target="head" />

            <div id="#{cc.clientId}" class="cc-checkbox cc-checkbox-value#{cc.attrs.value}"
                style="width: 14px; height: 14px;" onclick="checkboxToggleControl('#{cc.clientId}')"></div>
            <h:inputHidden id="value" value="#{cc.attrs.value}" />
        </div>
    </cc:implementation>
</ui:composition>

As you see (or not, because I didn't include the JS as it's not the problem), a litte JavaScript replaces the classes which the user sees as a three state checkbox (null, true, false). The current value is stored in <h:inputHidden> by JavaScript.

I would like to be able to register an ajax event when using the control in order to be able to store the value on the server. The component is located inside a <rich:extendedDataTable>. Every time the user clicks on it, the value should be stored "immediately" in the database. Using clientBehaviour with inputHidden doesn't work, as it doesn't support ajax.

  1. Is it possible to ajaxify <h:inputHidden> by using <h:inputText>? What are the drawbacks? (I would have to deal with validation, wouldn't I?)

  2. Is there another way to implement it as a composite component?

  3. Or, would it be "easier" to write a "real" component?

I'm not using <h:selectBooleanCheckbox> because it cannot be styled by CSS in the desired way and also because it doesn't support a three-state.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Augunrik
  • 1,866
  • 1
  • 21
  • 28
  • If you are using Primefaces you can take a look at their extension library http://fractalsoft.net/primeext-showcase-mojarra/views/triStateCheckbox.jsf , you can also get some inspiration from their implementation... – Daniel Nov 18 '13 at 08:57

1 Answers1

4

Indeed, the <f:ajax change> isn't supported on <h:inputHidden>. You'd only get the following exception because the HtmlInputHidden class behind the component doesn't implement ClientBehaviorHolder interface:

javax.faces.view.facelets.TagException: /test.xhtml @23,52 <f:ajax> Unable to attach <f:ajax> to non-ClientBehaviorHolder parent

You indeed need to replace it by a <h:inputText>. You can hide it using CSS display:none;. You only need to realize that manipulating its value by JavaScript won't automagically trigger any HTML DOM events. You'd need to manually trigger the change event by explicitly invoking onchange() on the HTML DOM element:

var input = document.getElementById(...);
input.value = newValue;
input.onchange();

Or, if you happen to use jQuery on the whole,

var $input = $("#...");
$input.val(newValue);
$input.change(); // Or $input.trigger("change");

Unrelated to the concrete problem, as to the question in the comment here:

<cc:attribute name="id" /><!-- necessary? -->

Only for documentary purposes. I.e. when you actually use shortDescription.

<cc:attribute name="id" shortDescription="The ID of the composite." />

But otherwise it's already inherited from the UIComponent base class (like binding and rendered).

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks for answering! As to the other questions in the question: Is there a major drawback of this approach. Or asked the other way around: Would you implement this fundamently differently? – Augunrik Nov 18 '13 at 16:29
  • As to when to create a composite or a custom component, head to http://stackoverflow.com/q/6822000/ I'd have preferred a custom component like ``: http://fractalsoft.net/primeext-showcase-mojarra/views/triStateCheckbox.jsf But if this is "too hard" for you and for internal usage only, a composite is of course also okay as long as it does the job you need. – BalusC Nov 18 '13 at 16:32