简体   繁体   English

每个请求都会重新创建复合组件中的支持bean

[英]Backing bean in composite component is recreated on every request

I have two variables "userId" and "name". 我有两个变量“userId”和“name”。 When I click for example the "SHOW USERID" button it works fine and sets "renderUserId=true" and it shows it with the "render", but then if I click the other "SHOW" button, the Bean is reconstruct and I loose the "renderUserId=true" and it becomes "false" and "renderName=true" so it shows ok, but the USERID is hidden. 当我点击例如“SHOW USERID”按钮时它工作正常并设置“renderUserId = true”并用“render”显示它,但是如果我点击另一个“SHOW”按钮,那么Bean重建并且我松了“renderUserId = true”,它变为“false”和“renderName = true”,因此它显示正常,但USERID被隐藏。

My question is, how can I avoid loosing the bean values when I render the xhtml? 我的问题是,当我渲染xhtml时,如何避免丢失bean值?

This is a simple simulation of my code. 这是我的代码的简单模拟。

NOTE: if I use "actionListener" instead of "f:setPropertyActionListener" in the "h:commandButton" I have the same result, the bean is reconstruct 注意:如果我在“h:commandButton”中使用“actionListener”而不是“f:setPropertyActionListener”我有相同的结果,那么bean就是重构

example.xhtml example.xhtml

<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"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:cc="http://java.sun.com/jsf/composite">

    <cc:interface componentType="com.bean.ExampleBean">
        <cc:attribute name="userId" type="java.lang.Integer"/>
        <cc:attribute name="name" type="java.lang.String"/>
    </cc:interface>

    <cc:implementation>

        <h:panelGrid id="example_panel" columns="1" width="100%">

            <h:outputText value="USERID: #{cc.attrs.userId}" rendered="#{!cc.attrs.renderUserId}"/>

            <a4j:commandButton value="SHOW USERID" render="example_panel"
                rendered="#{!cc.attrs.renderUserId}">
                <f:setPropertyActionListener value="#{true}"
                    target="#{cc.attrs.renderUserId}"/>
            </a4j:commandButton>                
            <a4j:commandButton value="HIDE USERID" render="example_panel"
                rendered="#{cc.attrs.renderUserId}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderUserId}"/>
            </a4j:commandButton>                


            <h:outputText value="NAME: #{cc.attrs.name}" rendered="#{!cc.attrs.renderName}"/>

            <a4j:commandButton value="SHOW NAME" render="example_panel"
                rendered="#{!cc.attrs.renderName}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderName}"/>
            </a4j:commandButton>                
            <a4j:commandButton value="HIDE NAME" render="example_panel"
                rendered="#{cc.attrs.renderName}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderName}"/>
            </a4j:commandButton>                


        </h:panelGrid>

    </cc:implementation>

</ui:composition>

ExampleBean.java ExampleBean.java

import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;


@FacesComponent("com.bean.ExampleBean")
public class ExampleBean extends UINamingContainer {

    private Integer userId;
    private String name;

    private boolean renderUserId;
    private boolean renderName;

}

There's a major misconception going here. 这里有一个重大的误解。 That's not a backing bean. 那不是支持bean。 That's a backing component. 这是一个支持组件。

JSF UI component instances are not view scoped, instead they are request scoped. JSF UI组件实例不是视图范围,而是请求范围。 They are destroyed by end of render response (after having saved their state into JSF view state) and recreated during view build time (and their state is restored from JSF view state). 它们在渲染响应结束时被破坏(在将状态保存到JSF视图状态之后)并在视图构建期间重新创建(并且它们的状态从JSF视图状态恢复)。

You've assigned the stateful properties as instance variables of the component. 您已将有状态属性指定为组件的实例变量。 This is not right. 这是不对的。 You should be explicitly storing them in the JSF state. 您应该明确地将它们存储在JSF状态中。 The correct approach for that is to let the getter and setter delegate to UIComponent#getStateHelper() . 正确的方法是让getter和setter委托给UIComponent#getStateHelper() Any attributes which are declared as <cc:attribute> already implicitly do that. 声明为<cc:attribute>任何属性都已隐式执行此操作。 You do absolutely not need to redeclare them as instance variables of the backing component. 您绝对不需要将它们重新声明为支持组件的实例变量。

Those booleans which are not declared as <cc:attribute> must be reimplemented like follows: 那些未声明为<cc:attribute>布尔值必须重新实现,如下所示:

public Boolean getRenderUserId() {
    return (Boolean) getStateHelper().eval("renderUserId", Boolean.FALSE);
}

public void setRenderUserId(Boolean renderUserId) {
    getStateHelper().put("renderUserId", renderUserId);
}

In your action(listener) method, just invoke setRenderUserId(true) accordingly. 在您的action(侦听器)方法中,只需相应地调用setRenderUserId(true)

Don't forget to fix the EL expressions accordingly: 不要忘记相应地修复EL表达式:

#{cc.renderUserId} 

See also: 也可以看看:

Your FacesComponent does not keep a state, it's a new instance everytime you call it. 你的FacesComponent没有保持状态,每次调用它都是一个新实例。 For your use case it seems you should use a ManagedBean with a view scope at least. 对于您的用例,您似乎应该至少使用具有视图范围的ManagedBean。 This means that as long as you're on the page with the buttons, the instance is kept. 这意味着只要您在带有按钮的页面上,就会保留实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM