【问题标题】:Dynamicly included page form first submit fails动态包含页面表单首次提交失败
【发布时间】:2012-07-21 10:57:22
【问题描述】:

我在动态包含的页面上有一个奇怪的行为

我的第一页有一个组合可以从不同的页面中选择:

        <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>

AnyPageBean 只返回以下 2 个页面之一:pilotNoTemplate.xhtml 或 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");
}

当我选择该页面(pipo 页面也是)时,pilotNoTemplate.xhtml 正确显示。

但是当我从 PilotNoTemplate.xhtml 提交表单时,JSF 认为提交是可以的, 但只有 Phase1 和 Phase 6 是跨越的,为什么? 我有一个 PhaseListener 显示阶段,它打印:

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

如果在表格中填写了任何内容,它会被重置...

第二次提交没问题,所有阶段都跨越了:

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

我的模板页面:

<!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>

合成页面,可以选择要包含的页面:

<!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>

pilotNoTemplate.xhtml 页面:

<?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>

我注意给xhtml页面中的每个组件都赋予“id”, 因为没有它会有那种问题,在我的情况下似乎是不够的。 有什么想法吗?

我看到其他人也有同样的问题,你找到解决办法了吗?

【问题讨论】:

    标签: ajax jsf-2 include viewparams


    【解决方案1】:

    这是由JSF spec issue 790 引起的,该错误发生在 JSF 2.0/2.1 中,并在即将发布的 JSF 2.2 中得到修复。基本上,当 JSF ajax 更新当前 &lt;h:form&gt; 之外的组件时,该组件又包含另一个 &lt;h:form&gt; 组件作为子组件,那么它的视图状态将不会更新(这是一个疏忽)。只有当您在 ajax 更新中明确指定表单组件时,它才会被更新。从理论上讲,您可以通过将 &lt;h:panelGroup id="panelForDynaPage"&gt; 替换为 &lt;h:form id="panelForDynaPage"&gt; 来解决它,但这在技术上是错误的标记。

    您可以使用以下 JS 来修复此错误。此脚本将为在 ajax 更新后未检索到任何视图状态的表单创建 javax.faces.ViewState 隐藏字段。

    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;
    }
    

    【讨论】:

    • 感谢您的回答。我尝试了解决方案1,将 替换为
      并从包含的页面中删除 => 提交是可以的,但是 不起作用...我尝试了 JS,但它似乎没有执行多次...我把它放在模板的 中,这可能不正确位置。我应该把它放在哪里?
    • 好的,我把脚本放在any.xhtml code code 。它具有相同的 pb 具有第一个解决方案:未设置 viewParam 并且未调用 #{pilotAction.prepare}。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多