Keeping @ViewScoped property states in composite component backings

人走茶凉 提交于 2019-12-10 20:56:34

问题


This question is related with my previous one about initializing composite components. Providing I've got a homegrown composite component and a @ViewScoped managed bean, I want certain properties of the managed bean to be reflected in the component. The transition of the property value to the component is done by a composite attribute.

That's the code I've got right now, consisting in a @ManagedBean which defines a random row number to be rendered at the component. The component generates a h:dataTable with a h:commandButton in each row, pressing that button just calls a method in the component backing code and performs a postback to the same view.

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

Given that components themselves are stateless, the row number to get rendered must be determined by the Bean instance in order not to loose its value when a postback happens.

Either the @ManagedBean and the component are initialized by an init() method which gets called when preRenderView event happens. The @ManagedBean checks it's not actually a postback request, considering it has to maintain the view state. The component however, being stateless, doesn't perform that checking because it loses its value in each request and have to be initialized in all of them.

The issue is clicking any of the h:commandButton makes an empty List to be rendered. It seems like it's being read before the init() method gets called. That doesn't happen however when a GET request is performed.

A way to avoid that issue is doing a lazy initialization at getValues() method, invoking init() method there instead of doing it from the view with a preRenderView event, despite I consider it not being the most elegant way to go with.

Using postAddToView instead of preRenderView event to initialize the component neither work, as it is invoked before the @ManagedBean gets initialized.


Tested in Mojarra 2.1.27 - 2.2.5 + Tomcat 7

来源:https://stackoverflow.com/questions/21549051/keeping-viewscoped-property-states-in-composite-component-backings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!