JSF2:为什么复合中的panelGroup渲染中的空测试会阻止动作被调用?

时间:2012-03-07 03:00:25

标签: jsf jsf-2

当自定义复合组件中的某些其他JSF元素与提交h:form的h:commandButton存在于同一页面中时,我在调用托管bean的操作时遇到问题。我可以将导致问题的页面元素缩小到那些执行以下无害(对我来说)渲染测试的人:

        <h:panelGroup rendered="#{not empty cc.attrs.element}">

或:

        <h:panelGroup rendered="#{empty cc.attrs.element}">

其中'element'是必需属性,由id从JPA实体加载。关于“空cc.attrs.element”或“非空cc.attrs.element”测试的内容会阻止正确的表单提交,并阻止在其出现的任何h:表单中调用操作。

表单提交页面debug.xhtml是:

<f:view>
    <f:metadata>
        <f:viewParam name="id" value="#{elementController.id}"/>
    </f:metadata>
</f:view>

<h:head>
    <title>DEBUG</title>
</h:head>
<h:body>
<h:form>        
<util:view_link element="#{elementController.selected}"/>
<h:commandButton value="Apply" action="#{elementController.reflect}" /> 
        </h:form>
    </h:body>

注意视图参数'id',通过ElementController.setId将强制通过JPA中的id加载'selected'或'current'元素实体,此处未显示。然后应该通过动作reflect():

移动到test.xhtml
@JSFaction
public Object reflect() {
    String $i = "reflect";
    log_debug($i);
    if (current==null) {
        log_warn($i, "can't generate reflect outcome for null current element");
        return null;
    }
    //return "reflect?faces-redirect=true&includeViewParams=true&id="+current.getId();
    //return "test?faces-redirect=true&includeViewParams=true&id="+current.getId();
    //return "reflect?id="+current.getId();
     return "/test?faces-redirect=true&id="+current.getId();
}

你可以看到上面我已经尝试过不同的结果URL,但事实证明问题与此无关,只是根本没有调用该行为(当{not empty cc.attrs.element时) } test在另一个页面元素中执行。

导致问题的复合组件是我的util:view_link ::

    <composite:interface>
    <composite:attribute name="element" required="true" type="com.greensoft.objectdb.test.entity.Element"/>
    <composite:attribute name="name" required="false"/>
    <composite:attribute name="showOwnerAsText" required="false" default="false"/>
    <composite:attribute name="showModelClass" required="false" default="false"/>
    <composite:attribute name="showEntityClass" required="false" default="false"/>
    <composite:attribute name="showId" default="false"/>
    <!--composite:attribute name="showHelp" required="false" default="false"/-->
    <!--composite:attribute name="title" required="false"/-->
</composite:interface>

<composite:implementation>

    <h:panelGroup rendered="#{empty cc.attrs.element}">
        <span class="warn">NULL</span>
    </h:panelGroup>

    <h:panelGroup rendered="#{not empty cc.attrs.element}">
        <h:panelGroup rendered="#{cc.attrs.showOwnerAsText}">
            <h:outputText style="font-weight:bold" value="#{cc.attrs.element.owner.name}."/>
        </h:panelGroup>

        <h:panelGroup rendered="#{cc.attrs.showId}">
            <h:link outcome="/view" value="&lt;#{cc.attrs.element.id}&gt;">
                <f:param name="id" value="#{cc.attrs.element.id}"/>
            </h:link>
        </h:panelGroup>

        <h:link outcome="/view" 
                value="#{empty cc.attrs.name ? cc.attrs.element.name: cc.attrs.name}"
                >
            <f:param name="id" value="#{cc.attrs.element.id}"/>
        </h:link>
    </h:panelGroup>
</composite:implementation>

您可以看到这只是创建每个元素链接的可重用方法,数据库ID作为参数传递。如果没有元素,则显示“NULL”。

通过一次注释掉比特(包围)我可以证明问题与render =“#{empty cc.attrs.element}和render =”#{not empty cc.attrs.element} test有关这两个都阻止了表单提交和操作啊:commandButton在同一个组合页面中调用(参见上面的debug.xhtml)。例如,如果我只使用普通的h:panelGroup元素而没有'rendered =“#{empty cc.attrs.element}'和'rendered =”#{not empty cc.attrs.element}'test,那么一切都很好,并且h:commandButton按预期工作,操作被称为ok,导航工作正常!

问:为什么测试'rendered =“#{empty cc.attrs.element}'或'rendered =”#{not empty cc.attrs.element}'阻止正确的表单提交并停止调用该操作?

感谢创意,Webel

编辑:问题只发生在我对elementController.selected进行测试时,其中.selected是一个@Entity元素。我尝试使用一个带有String name属性的简单Duummy类和一个@RequestScoped DebugController,它运行正常(该动作被调用)。

<h:panelGroup rendered="#{not empty debugController.dummy}">
<h:outputText value="#{debugController.dummy.name}"/>
</h:panelGroup>
<h:commandButton value="Apply" action="#{elementController.reflect}" /> 

报告为错误:http://java.net/jira/browse/JAVASERVERFACES-2343

1 个答案:

答案 0 :(得分:0)

我现在可以部分回答这个问题了。我现在可以看到正在发生的事情并对其进行描述,但我仍然无法完全理解 - 就JSF阶段和请求范围而言 - 为什么会发生这种情况。这是一个微妙而棘手的问题。

为了解释它,我重写了测试用例以使其更简单。带有id查询参数的view.xhtml测试页面是:

<f:view>
    <f:metadata>
        <f:viewParam name="id" value="#{elementController.id}"/>
    </f:metadata>
</f:view>
<h:body>   
    <h:form>

       <h:panelGroup rendered="#{empty elementController.selected}">
           <div style="color:orange;">
               No Element found for id(#{elementController.id}) or page called without id query param.
           </div>
       </h:panelGroup>

       <h:panelGroup rendered="#{not empty elementController.selected}">
           [<h:outputText value="#{elementController.selected.id}"/>]&nbsp;
           <h:outputText value="#{elementController.selected.name}"/>
       </h:panelGroup>

        <br/><br/>

       <h:commandButton value="Apply" action="#{elementController.action}" />

    </h:form>
</h:body>

ElementController扩展RequestController。在加载view.xhtml时,id查询参数使用setId设置为viewParam,后者通过id(来自数据库)加载匹配的Element实体,并将其设置为'current'(又名“selected”)元素,可用作getSelected( ),它还对零电流进行了测试,这就是导致问题的原因:

public void setId(Long id) {
    log_debug("setId","id",id);
    if (id != null) {
        this.id = id;
        T found = (T) getAbstractFacade().find(id);
        if (found == null) {
            String $error = "No object with id(" + id + ") found for class " + getManagedClass().getSimpleName();
            log_error($error);
        }
        setCurrent(found);
    }
}

public T getSelected() {
    log_debug("getSelected","current",current);        
    if (current == null) {
        //current = newElement(); //THIS WAS CAUSING PROBLEMS
        log_warn("getSelected","null current Element");
    }
    return current;
}

问题在于,在提交表单时,为每个测试实例'#{not empty elementController.selected}'或'#{empty elementController.selected}'调用了两次getSelected()该行动被称为,字段为'current'null

在@RequestScoped @ManagedBean ElementController中,getSelected()方法测试当前(也就是选定的)Element是否为null,并且正在采取导致错误的操作,这是对操作调用的短路当调用'not empty elementController.selected'时,在commandButton上:

(我看到的问题与“选定的”元素是否经过'非空'或'空'测试是否是@Entity无关,我误导相信因为问题是处理Element实体的ElementController,当然这个结论听起来并不正确。)

如果我摆脱导致错误的newElement()的赋值,并且在没有id参数的情况下调用视图页面时触发了这种情况(触发了id加载'current'实体)而是记录一个警告,我可以看到,对于'not empty elementController.selected'或'empty elementController.selected'测试的每个实例,在表单提交时调用commandButton的操作之前,会记录两次该警告。 / p>

不知怎的 - 这是我不完全理解的部分 - 在@RequestScoped下,分配给变量'current'(又名“selected”)的Element丢失了,'not empty elementController.selected'test in使用current = null(带副作用)调用视图页面(以及getSelected())两次。

如果我将ElementController的范围更改为@SessionScoped,问题就会消失;保持当前的Element状态,并且永远不会使用'current'null调用getSelected()。

我将非常感谢任何回复评论,这些评论根据JSF阶段和请求范围解释为什么我的'当前'变量突然为空以及为什么'#{not empty elementController.selected}'执行两次之前该动作被调用。我已经检查过中间没有调用setId(仅在初始视图页面加载时),其他东西正在将当前重置为null,或者在表单提交和调用操作之间重新创建请求范围bean。