2

I've browsed SO, and found some answers that have guided me closer to getting working radio buttons, but I'm stuck now.

I have the buttons, but am unable to get the value of the selected one.

I'm using JSF, hence the #{searchFlightsBean.setDir()}

Here's what I currently have:

<h:panelGrid>
    <div class="btn-group" data-toggle-name="is_private" data-toggle="buttons-radio" >
        <button type="button" value="0" class="btn" data-toggle="button">Public</button>
        <button type="button" value="1" class="btn" data-toggle="button">Private</button>
    </div>
    <h:inputHidden id="hiddenDir" value="0" valueChangeListener="#{searchFlightsBean.setDir()}"  onchange="submit()"/>
</h:panelGrid>

<script>
$(function() {
    $('div.btn-group[data-toggle-name]').each(function() {
        var group = $(this);
        var form = group.parents('form').eq(0);
        var name = group.attr('data-toggle-name');
        var hidden = $('input[name="' + name + '"]', form);
        $('button', group).each(function() {
            var button = $(this);
            button.on('click', function() {
                hidden.val($(this).val());
            });
            if (button.val() == hidden.val()) {
                button.addClass('active');
                #{searchFlightsBean.setDir(button.value)}
            }
        });
    });
});
</script>

In my bean, when setDir() is called, I am logging the value that it receives, like so:

public void setDir(ValueChangeEvent e) {
    this.dir = e.getNewValue().toString();
    log.info("NEW DIRECTION: " + this.getDir());
}

It doesn't log - setDir() is never called. For some reason the valueChangeListener attribute on the h:inputHidden tag doesn't work. Am I missing something?

Skytiger
  • 1,745
  • 4
  • 26
  • 53
  • Did the answer of noone really solve your problem? To me, he seems to just have posted a completely random solution which applies more to JSF 1.x and not to JSF 2.x and yet you marked his answer accepted. Are you *really* using JSF 1.x with RichFaces 3.x? Nothing in your previously asked questions indicates that. You should not mark an answer accepted if you still struggle with the same main problem and thus the answer wasn't helpful at all. – BalusC Aug 22 '13 at 12:33
  • @BalusC No, it didn't really answer my question - I unaccepted it. I've also edited my question to bring it up to date with what I'm currently trying to do. Either I'm using the valueChangeListener wrong, or I need to use something else. – Skytiger Aug 22 '13 at 12:42
  • Okay. Just to confirm, you're using JSF 2.x? Thus, you can use ``? – BalusC Aug 22 '13 at 14:11

2 Answers2

1

You cannot just call any backing bean functions via Expression Language calls (#{...}) in JavaScript.

What you could do, is using a4j:jsFunction to offer your bean method to the java script code. That might look like this:

<a4j:jsFunction name="setDir" action="#{searchFlightsBean.setDir()}" ajaxSingle="true">
    <a4j:param name="dir" assignTo="#{searchFlightsBean.dir}" />
</a4j:jsFunction>

See http://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html/a4j_jsFunction.html and http://showcase.richfaces.org/richfaces/component-sample.jsf?demo=jsFunction&skin=blueSky

noone
  • 19,520
  • 5
  • 61
  • 76
  • Thanks. I just noticed that I'm getting a JS error (apparently I can't call `button.live`. How would I call the ajax from my JS? – Skytiger Aug 22 '13 at 09:15
  • @Skytiger See the examples of the showcase. But you need to use the A4J library, which is included in RichFaces. You need to use some extension to handle the bridging between JS and JSF, otherwise you need some not-so-nice hacks. See http://stackoverflow.com/questions/3710908/jsf-2-0-ajax-call-a-bean-method-from-javascript-with-jsf-ajax-request-or-some – noone Aug 22 '13 at 09:20
  • Thanks, but the A4J library isn't going to cut it for me. I decided to try another workaround. – Skytiger Aug 22 '13 at 12:41
1

Your concrete problem is caused because you used valueChangeListener the wrong way.

<h:inputHidden ... valueChangeListener="#{searchFlightsBean.setDir()}">

This does not match the method signature. You should omit the parentheses ().

<h:inputHidden ... valueChangeListener="#{searchFlightsBean.setDir}">

Otherwise JSF expects an argumentless setDir() method. Then, you're nowhere in JavaScript triggering the change event on the input element. The onchange="submit()" is therefore never invoked. You should be doing hidden.trigger("change") in JS to achieve that.

But, after all, this is somewhat clumsy. You're sending a full synchronous request and your JS code is rather overcomplicated (and stops working once you ajax-update the form). Provided that you're indeeed using JSF 2.x, I suggest to bring in <f:ajax> — which unfortunately doesn't work in <h:inputHidden>, hence the <h:inputText style="display:none"> — and to make use of $.on() in jQuery to keep the functions working even when you ajax-update the DOM.

<h:form>
    <div class="btn-group" data-toggle-name="is_private" data-toggle="buttons-radio" >
        <button type="button" value="0" class="btn" data-toggle="button">Public</button>
        <button type="button" value="1" class="btn" data-toggle="button">Private</button>
    </div>
    <h:inputText id="is_private" value="#{bean.dir}" style="display: none;">
        <f:ajax listener="#{bean.changeDir}" />
    </h:inputText>
    <!-- Note: <h:inputText id> must be exactly the same as <div data-toggle-name> -->
</h:form>

<h:outputScript>
    $(document).on("click", "[data-toggle=buttons-radio] button", function() {
        var $button = $(this);
        var id = $button.closest(".btn-group").attr("data-toggle-name");
        var $input = $button.closest("form").find("input[id$=':" + id + "']");

        if ($input.val() != $button.val()) {
            $input.val($button.val()).trigger("change");
        }
    });
</h:outputScript>

(noted should be that the whole script should really be placed in its own .js file which you include by <h:outputScript name="some.js" target="body">; note that you don't need $(document).ready() nor $(function() {}) mess; also note that the very JS function is reusable on all other <div data-toggle="buttons-radio"> groups without changes)

With this bean:

private Integer dir;

public void changeDir() {
    System.out.println("New direction: " + dir);
}

// ...

(noted should be that when you're doing further nothing relevant in changeDir() method, then you could just omit the whole method and <f:ajax> altogether and revert <h:inputText style="display:none"> back to <h:inputHidden> and remove .trigger("change") and rely on the regular form submit. It'll work as good)

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555