1

I got a strange behaviour on dynamicly included page

My first page has a combo to choose from different pages to include :

        <h:panelGrid columns="2">
            <h:outputLabel value="Choose your page to include" />
            <h:selectOneMenu id="pageList" value="#{anyPageBean.page}">
                <f:selectItems value="#{anyPageBean.pageList}" var="w" itemValue="#{w}" itemLabel="#{w}" />
            </h:selectOneMenu>
        </h:panelGrid>

The AnyPageBean just gives back one of the 2 following pages : pilotNoTemplate.xhtml or pipo.xhtml

@ManagedBean
@SessionScoped
public class AnyPageBean {

private String page;
private java.util.List<String> pageList;

public AnyPageBean() {
    super();
    pageList = new ArrayList<String>();
    pageList.add("pilotNoTemplate.xhtml");
    pageList.add("pipo.xhtml");
}

The pilotNoTemplate.xhtml appears correctly when i choose that page (the pipo page also).

But when i submit the form from pilotNoTemplate.xhtml, JSF considers that submit as a POST which is OK, but only Phase1 and Phase 6 are spanned, WHY ? I have a PhaseListener showing the Phases, it prints :

INFO  IN pour /jsf2-todo-001-dynamic-page-include-v2/any.faces ¤¤¤¤¤ POST
INFO  AJAX BEFORE RESTORE_VIEW 1 for viewId=null 
INFO  AJAX AFTER  RESTORE_VIEW 1 for viewId=/any.xhtml
INFO  AJAX BEFORE RENDER_RESPONSE 6 for viewId=/any.xhtml 
INFO  AJAX AFTER  RENDER_RESPONSE 6 for viewId=/any.xhtml

If anything was filled in the form, it gets resetted ...

the SECOND submit is all right, all the phase are spanned :

INFO IN pour /jsf2-todo-001-dynamic-page-include-v2/any.faces ¤¤¤¤¤ POST
INFO AJAX BEFORE RESTORE_VIEW 1 for viewId=null 
INFO AJAX AFTER  RESTORE_VIEW 1 for viewId=/any.xhtml
INFO AJAX BEFORE APPLY_REQUEST_VALUES 2 for viewId=/any.xhtml 
INFO AJAX AFTER  APPLY_REQUEST_VALUES 2 for viewId=/any.xhtml
INFO AJAX BEFORE PROCESS_VALIDATIONS 3 for viewId=/any.xhtml 
INFO AJAX AFTER  PROCESS_VALIDATIONS 3 for viewId=/any.xhtml
INFO AJAX BEFORE UPDATE_MODEL_VALUES 4 for viewId=/any.xhtml 
INFO AJAX AFTER  UPDATE_MODEL_VALUES 4 for viewId=/any.xhtml
INFO AJAX BEFORE INVOKE_APPLICATION 5 for viewId=/any.xhtml 
INFO AJAX AFTER  INVOKE_APPLICATION 5 for viewId=/any.xhtml
INFO AJAX BEFORE RENDER_RESPONSE 6 for viewId=/any.xhtml 
INFO AJAX AFTER  RENDER_RESPONSE 6 for viewId=/any.xhtml

My template page :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
    <meta http-equiv="Cache-Control" content="no-store" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="0" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</h:head>
<h:body>
    <div>
        <h:panelGroup id="globalMessages">
            <h:messages globalOnly="false" styleClass="messages" />
        </h:panelGroup>
        <ui:insert name="content" />
    </div>
</h:body>
</html>

the composition page, enabling to choose a page to include :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" >

<ui:composition template="/layout/template.xhtml">
    <ui:define name="content">
        <h:form id="chooseForm">
            <h:panelGrid columns="2">
                <h:outputLabel value="Choose your page to include" />
                <h:selectOneMenu id="pageList" value="#{anyPageBean.page}">
                    <f:selectItems value="#{anyPageBean.pageList}" var="w" itemValue="#{w}" itemLabel="#{w}" />
                </h:selectOneMenu>
            </h:panelGrid>
            <h:commandButton id="show" value="Show page">
                <f:ajax event="click" execute="@form" render=":panelForDynaPage  :globalMessages" />
            </h:commandButton>
        </h:form>

        <h:panelGroup id="panelForDynaPage">
            <ui:include id="includeContenu" src="#{anyPageBean.page}" />
        </h:panelGroup>

    </ui:define>
</ui:composition>
</html>

the pilotNoTemplate.xhtml page :

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition  xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
    <f:metadata>
        <f:viewParam id="b" name="id" value="#{pilotAction.id}" />
    </f:metadata>
    <f:event id="a" listener="#{pilotAction.prepare}" type="preRenderView" />

    <h:form id="formPilot">
        <h:panelGrid columns="2">
            <h:outputLabel value="#{appMsg['pilot.firstname']}" />
            <h:inputText id="firstname" label="#{appMsg['pilot.firstname']}" value="#{pilotHolder.pilot.firstname}"
                required="true" />
            <h:outputLabel value="#{appMsg['pilot.lastname']}" />
            <h:inputText id="lastname" label="#{appMsg['pilot.lastname']}" value="#{pilotHolder.pilot.lastname}" />
            <h:outputLabel value="#{appMsg['pilot.age']}" />
            <h:inputText id="age" label="#{appMsg['pilot.age']}" value="#{pilotHolder.pilot.age}" required="true"
                validator="#{pilotAction.validateAge}" />
        </h:panelGrid>
        <h:commandButton id="merge" action="#{pilotAction.doMerge}"
            value="#{pilotHolder.pilot.id ne null ? appMsg['pilot.update'] : appMsg['pilot.create']}">
            <f:ajax id="ajaxm" event="click" execute="@form" render=":panelForDynaPage :globalMessages" />
        </h:commandButton>
    </h:form>
</ui:composition>

I paid attention to gives "id" to every component in the xhtml page, since there are that kind of problems without it, is seems to be unsuffient in my case. any idea ?

I 've seen others having the same problem, did you find any solution ?

Gauthier Peel
  • 1,438
  • 2
  • 17
  • 35

1 Answers1

0

This is caused by JSF spec issue 790, a bug which occurs in JSF 2.0/2.1 and is fixed in the upcoming JSF 2.2. Basically, when JSF ajax updates outside the current <h:form> a component which in turn contains another <h:form> component as child, then its view state won't be updated (this is an oversight). It would only be updated when you explicitly specify the form component in the ajax update. You could theoretically solve it by replacing <h:panelGroup id="panelForDynaPage"> by a <h:form id="panelForDynaPage">, but this is technically the wrong markup.

You can use the following JS to fix this bug. This script will create the javax.faces.ViewState hidden field for forms which did not retrieve any view state after ajax update.

var ie = /*@cc_on!@*/false;

jsf.ajax.addOnEvent(function(data) {
    if (data.status == "success") {
        var viewState = document.getElementsByName("javax.faces.ViewState")[0].value;

        for (var i = 0; i < document.forms.length; i++) {
            var form = document.forms[i];

            if (!hasViewState(form)) {
                var hidden = document.createElement(ie ? "<input name='javax.faces.ViewState'>" : "input");
                hidden.setAttribute("type", "hidden");
                hidden.setAttribute("name", "javax.faces.ViewState");
                hidden.setAttribute("value", viewState);
                hidden.setAttribute("autocomplete", "off");
                form.appendChild(hidden);
            }
        }
    }
});

function hasViewState(form) {
    for (var i = 0; i < form.elements.length; i++) {
        if (form.elements[i].name == "javax.faces.ViewState") {
            return true;
        }
    }

    return false;
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks for your answer. I tried solution1 by replacing by a and deleting the from the included page => the submits are OK, but and do not work ... I tried the JS but it does not seem to execute more than once ... i put it in the of the template which may not be the right position. where should i place it ? – Gauthier Peel Sep 04 '12 at 13:43
  • ok, i placed the script on the AJAX callback in any.xhtml `code` `code` . It has the same pb has the first solution : the viewParam is not set and the #{pilotAction.prepare} is not called. – Gauthier Peel Sep 07 '12 at 06:58