views:

568

answers:

1

Some background: I am building a custom JSF component. The component is basically a text editor and it should have a "Save" -button for saving the content string of the editor. As I am using the CodeMirror library, I need to fetch the content (string) from the editor with javascript and send that to the server. Therefore, in this case I cannot use XML-based JS invocation such as f:ajax.

The question: I was planning to send the string with jsf.ajax.request, but it doesn't directly support calling methods on beans. How can I invoke a method in a bean with JSF in AJAX manner?

There at least two ways to get around this:

  • Include a hidden form to page with hidden inputfield. Update that inputfield from javascript and then call jsf.ajax.request to post that form. Custom actions can be invoced in the property's getter or setter if needed.
  • Do the request with raw XMLHttpRequest (or maybe with help from some other JS library). Create a servlet and call that.

Both ways are clumsy and the latter also breaks out of JSF scope.

Am I missing something? How do you do these?

There is a quite similar question, but the answers given only refer to XML-based AJAX invocations. There is also another similar question, but that refers to XML-based AJAX calls as well.

+1  A: 

I couldn't find out how to call beans direcly with javascript, but here is a hack around calling f:ajax-declaration from javascript:

1) Create a hidden form with fields for all the data that you want to send to the server. Include a h:commandButton as well:

<h:form id="hiddenForm" style="display: none;">
    <h:inputHidden id="someData" value="#{someBean.someData}" />
    <h:commandButton id="invisibleClickTarget" />
        <f:ajax execute="@form" listener="#{someBean.myCoolActionOnServer()}" />
    </h:commandButton>
</h:form>

As usual, listener attribute, #{someBean.myCoolActionOnServer()} in this case, refers to the method that you want to execute on the server.

2) In some other button use onclick to call for your special javascript AND click the trigger-button via javascript:

<h:commandButton value="Click me" onclick="populateTheForm('hiddenForm'); document.getElementById('hiddenForm:invisibleClickTarget').click(); return false;" />

populateTheForm() should actually fill the data into hiddenForm's fields.

This is a simplification of my case but should work. Still looking for more conventient approach, though.

Tuukka Mustonen
As an alternative you could also just leave away the `<h:commandButton>` and use `<f:ajax>` inside `<h:inputHidden>` which get executed on `change` event. That's less code.
BalusC
Yeah, that should work if user wants to transfer just one field (well, in this case I do). With multiple fields within the form, I guess that might trigger unwanted behavior, with AJAX call launching before rest of the fields could be updated.
Tuukka Mustonen
...unless one puts that `f:ajax` for the field that is updated last. Yup, that would do the trick. Thanks for your suggestions once again, BalusC.
Tuukka Mustonen
I tried this by putting the `f:ajax` directly inside `h:inputHidden`. The result is '<f:ajax> Unable to attach <f:ajax> to non-ClientBehaviorHolder parent'. So this is not possible.
Tuukka Mustonen