在复合组件背景中保持@ViewScoped属性状态

时间:2014-02-04 09:54:36

标签: jsf jsf-2 composite-component

这个问题与我之前关于initializing composite components的问题有关。假设我有一个自己开发的复合组件和一个@ViewScoped托管bean,我希望托管bean的某些属性能够反映在组件中。属性值到组件的转换由复合属性完成。

这就是我现在所拥有的代码,包含@ManagedBean,它定义了要在组件上呈现的随机行号。该组件在每一行中生成h:dataTable h:commandButton,按下该按钮只调用组件支持代码中的方法,执行回发到同一视图

托管bean:

@ManagedBean
@ViewScoped
public class Bean implements Serializable {

    private Integer rowNum;

    public Integer getRowNum() {
        return rowNum;
    }

    /**
     * Initializes the rowNum property with a value between 1 and 9
     */
    public void init() {
        if (!FacesContext.getCurrentInstance().isPostback()) {
            rowNum = new Random().nextInt(10) + 1;
            System.out.println("Bean initialized");
        }
    }

    public void setRowNum(Integer rowNum) {
        this.rowNum = rowNum;
    }

}

的index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:comp="http://java.sun.com/jsf/composite/comp">
<h:head />
<body>
    <f:event type="preRenderView" listener="#{bean.init}" />
    <h:form>
        <comp:myTable itemNumber="#{bean.rowNum}" />
    </h:form>
</body>
</html>

MyTable.java:

@FacesComponent("components.myTable")
public class MyTable extends UINamingContainer {

    private List<String> values = new ArrayList<String>();

    public void action() {
        System.out.println("Called");
    }

    public List<String> getValues() {
        return values;
    }

    /**
     * Initializes the 'values' List based in 'itemNumber' attribute
     */
    public void init() {
        System.out.println("Component initialized");
        Integer num = (Integer) getAttributes().get("itemNumber");
        for (int i = 0; i < num; i++) {
            values.add("item" + i);
        }
    }

}

myTable.xhtml:

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

<h:body>
    <composite:interface componentType="components.myTable">
        <composite:attribute name="itemNumber" 
            type="java.lang.Integer" required="true" />
    </composite:interface>

    <composite:implementation>
        <f:event type="preRenderView" listener="#{cc.init}" />
        <h:dataTable value="#{cc.values}" var="value">
            <h:column headerText="column">
                #{value}
                <h:commandButton value="Action" action="#{cc.action}" />
            </h:column>
        </h:dataTable>
    </composite:implementation>
</h:body>
</html>

鉴于components themselves are stateless,要呈现的行号必须由Bean实例确定,以便在发生回发时不会丢失其值。

@ManagedBean和组件由init()方法初始化,当preRenderView事件发生时,该方法被调用。考虑到它必须保持视图状态,@ManagedBean检查它实际上不是回发请求。然而,该组件是无状态的,不执行该检查,因为它在每个请求中丢失其值并且必须在所有请求中初始化。

问题是点击h:commandButton中的任何一个会使空List呈现。在调用init()方法之前,似乎正在阅读它。但是,当执行 GET 请求时,情况不会发生。

避免该问题的一种方法是在getValues()方法进行延迟初始化,在init()方法中调用preRenderView方法,而不是使用postAddToView事件从视图中进行,尽管我认为它不是最优雅的方式。

使用preRenderView代替@ManagedBean事件来初始化组件既不起作用,因为它在{{1}}初始化之前被调用。


在Mojarra 2.1.27中测试 - 2.2.5 + Tomcat 7

0 个答案:

没有答案