14

I have

<h:form>
    <h:commandLink action="#{my_fake_ajax_link}">
        <h:outputText value="Link" />
        <f:ajax render=":mydiv" />
    </h:commandLink>
</h:form>

<h:panelGroup layout="block" id="mydiv">
    <h:form>
        <h:commandLink action="#{mybean.delete(0)}">
            <h:outputText value="Here" />
            <f:ajax render="@form" />
        </h:commandLink>
    </h:form>
</h:panelGroup>

When I click once on "my_fake_ajax_link", then I have to click twice on "delete" link. This is only an example. I don't have this real case. I have multiple forms on a page and I can't just add all of them in a single form.

I checked what the problem is and it is:

  • When you click on "my_fake_ajax_link", the mydiv refreshs with ajax as it should.
  • ViewState of the refreshed form on ajax is missing.

How can I add the ViewState? How can I make it work without using only one form? This looks like an JSF bug to me. I don't want to refresh that div automatically with

jQuery("#mydiv").load(document.location.href);

but I will in my worst case possible.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
Alex
  • 2,094
  • 3
  • 24
  • 42

2 Answers2

23

This is a known problem in the auto-included jsf.js library of JSF which handles ajax responses. See also JSF spec issue 790 which is fixed in the upcoming JSF 2.3. In the meanwhile, with JSF 2.0/2.1/2.2, you have to explicitly specify the ID of the other <h:form> inside the render attribtue to trigger proper addition of the view state.

<h:form>
    <h:commandLink action="#{my_fake_ajax_link}">
        <h:outputText value="Link" />
        <f:ajax render=":mydiv :mydivForm" />
    </h:commandLink>
</h:form>

<h:panelGroup layout="block" id="mydiv">
    <h:form id="mydivForm">
        <h:commandLink action="#{mybean.delete(0)}">
            <h:outputText value="Here" />
            <f:ajax render="@form" />
        </h:commandLink>
    </h:form>
</h:panelGroup>

No, this does not cause any overhead or markup duplication in the ajax response. Alternatively, use OmniFaces fixviewstate.js.

See also:

Community
  • 1
  • 1
BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
0

Workaround for that:

First you need to set an onevent handler for the button that sends the ajax request:

<h:form id="newComment">
    <h:commandButton id="saveNewComment" action="#{postBean.actionSaveNewCommentAjax}" value="#{rb['speichern']}">
        <f:ajax execute="@form" render=":commentsBox" onevent="function(data) { fixOtherFormsViewState(data, 'newComment') }" />
    </h:commandButton>
</h:form>

Then you need to declare this javascript methods that will take the correct viewState value and add it to all forms which doesn't have it:

<h:outputScript>
function fixOtherFormsViewState(data, goodFormId) {
    if (data.status != "success") {
        return;
    }

    var viewState = jQuery("#" + goodFormId + " input[name='javax.faces.ViewState']").val();
    jQuery("form:not(:contains(input[name='javax.faces.ViewState']))").each(function (idx, elem) {
        var form = jQuery(elem);
        var input = jQuery("&lt;input type='hidden' name='javax.faces.ViewState' /&gt;").val(viewState);
        form.append(input);
    });
}
</h:outputScript>
Igor Mukhin
  • 13,058
  • 17
  • 48
  • 60