wujek
wujek

Reputation: 11040

How to use jsf.ajax.request to manually send ajax request in JSF

I have a table and each row has the 'onclick' configured to call a js function - this part works. I would like the function to issue an ajax request to a method that I can somehow define. Upon return, a div below the table should be rendered. Is this possible? I tried the JS function with the code:

<h:outputScript library="javax.faces" name="jsf.js" />
...
function clickAndRender() {
  jsf.ajax.request(this, event, {render: 'div-to-render'})
}

But it doesn't work of course. I have no idea what the values of the 'source' parameter (the code above uses 'this') should be, neither do I know what execute in the last parameter should be set to. I also don't know how to define the url to be called and the 'action' method.

The solution could also be using some invisible form somewhere else in the page that get's submitted by the click. Can anybody help?

Upvotes: 5

Views: 22307

Answers (3)

BalusC
BalusC

Reputation: 1108722

As per JSF 2.3, you can use <h:commandScript> for this. See also spec issue 613.

<h:form>
    <h:commandScript name="foo" action="#{bean.action}" render="div-to-render" />
</h:form>
function clickAndRender() {
    foo();
}

It's available since Mojarra 2.3.0-m06.


This answer was updated as above 3 years after the question was posted. Below is the original answer when which dates back when <h:commandScript> didn't exist.


I have no idea what the values of the 'source' parameter (the code above uses 'this') should be

It should be the client ID of the UICommand component which you'd like to invoke, e.g. <h:commandLink>, <h:commandButton>, etc as provided by standard JSF API. This way JSF will during apply request values phase be able to locate the desired component in the component tree and then queue all of the action(listener)s registered on it.


neither do I know what execute in the last parameter should be set to.

It should be the space-separated client IDs of the components which you'd like to process during the form submit. It's exactly the same as you'd normally use in <f:ajax execute>.


I also don't know how to define the url to be called

Just the current page, as derived from the <form> which is been generated by the <h:form> where the UICommand component is nested in. But you don't need to specify it in jsf.ajax.request(). The jsf.js will already determine it based on the client ID of the UICommand component.


and the 'action' method.

Just specify the action or actionListener attribute of the target UICommand component the usual way.


All with all, your concrete problem is likely that you don't have any UICommand component in the view, so you can't use jsf.ajax.request(). One way would be to have a form with a command component which is hidden by CSS display:none:

<h:form id="foo" style="display:none">
    <h:commandLink id="bar" action="#{bean.method}" />
</h:form>

Then you can use foo:bar as source parameter.

jsf.ajax.request('foo:bar', null, {'javax.faces.behavior.event': 'action', 'execute': '@form', 'render': 'div-to-render'});

The above does effectively the same as <f:ajax execute="@form" render="div-to-render"> in the command component. You could alternatively also go this path and then use document.getElementById("foo:bar").click() instead of jsf.ajax.request().

Alternatively, you can also create a custom UICommand component which makes it all a bit easier for JavaScript users. The JSF utility library OmniFaces has such a component, the <o:commandScript>. See also its showcase page and source code. The JSF component library PrimeFaces has also a similar component in flavor of <p:remoteCommand>. See also its showcase page. RichFaces has a <a4j:jsFunction> which does the same. See also its showcase page.

Upvotes: 13

wujek
wujek

Reputation: 11040

Ok, so I managed to implement what I need, with all the ways suggested by BalusC, with <a4j:jsFunction> looking the best by a wide margin. The reason I am writing a follow up is that there were more things wrong which I initially didn't include in the question and there is not enough comment space ;d

  1. My form was in the wrong place - it was placed within the 'table':

    <table>
      <ui:repeat>...</ui:repeat>
      <form>...</form>
    </table>
    

    Whereas it works for non-Ajax, id doesn't work for Ajax requests. Moving it right below the table caused the requests to be submitted.

  2. The panel that I wanted to render was rendered='false' initially, which caused the HTML markup not to be output in the first place. When the Ajax call returned, it couldn't find an element with the id I wanted to render, and resulted in error dialogs or nothing at all (depending on the method). The solution was to use a wrapper div with the id in question that was always rendered, and use conditional rendering only within that div. So, instead of:

    <h:outputText id="to-render" rendered="${bean.clicked != null}">
      ...
    </h:outputText>
    

    I needed to do this:

    <h:panelGroup layout="block" id="to-render">
      <h:outputText rendered="${bean.clicked != null}">
        ...
      </h:outputText>
    </h:panelGroup>
    

Thanks for all the help. I will leave the post by BalusC as the correct one as it was really correct, with my own errors influencing the whole thing.

Upvotes: 0

Mr.J4mes
Mr.J4mes

Reputation: 9266

Using native JSF alone, I have no idea how to do that. However, you should take a look at PrimeFace's RemoteCommand. It's exactly what you need at the moment. It would be something like this:

<p:remoteCommand name="onRowClick" actionListener="myBean.onRowClick" update="div-to-render" />

<h:someComponent onclick="onRowClick" />

Upvotes: 0

Related Questions