【问题标题】:How to prevent multiple composite components reset themselves on a JSF page?如何防止多个复合组件在 JSF 页面上自行重置?
【发布时间】:2012-11-22 01:08:15
【问题描述】:

我把这个问题放在一个简单的例子中,一个复合组件,计算 2 个输入的总和并将结果打印到 outputText 中

JSF 主页:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ez="http://java.sun.com/jsf/composite/ezcomp/">
    <h:head></h:head>
    <h:body>
      <ez:Calculator />
      <br/>
      <br/>
      <ez:Calculator />
      <br/>
      <br/>
      <ez:Calculator />
    </h:body>
</html>

复合组件 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:composite="http://java.sun.com/jsf/composite"
  xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>This content will not be displayed</title>
    </h:head>
    <h:body>
    <composite:interface componentType="calculator">
    </composite:interface>

    <composite:implementation>
      <h:form>
      <h:inputText id="first" value="#{cc.firstNumber}" /> 
      <h:commandButton value="+" action="#{cc.sum}"/>
      <h:inputText id="second" value="#{cc.secondNumber}" /> 
      </h:form>
      <h:outputText id="result" value="#{cc.result}" />
  </composite:implementation>

</h:body>
</html>

复合组件支持bean:

package ez;


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

@FacesComponent("calculator")
public class Calculator extends UINamingContainer  {

    private Long firstNumber;
    private Long secondNumber;
    private Long result;

    public Calculator() {
    }

    @Override
    public String getFamily() { 
      return "javax.faces.NamingContainer"; 
    }

    public void setFirstNumber(String firstNumber) {
      this.firstNumber = Long.parseLong(firstNumber);
    }

    public String getFirstNumber() {
      if(firstNumber == null) {
        return null;
      }
      return firstNumber.toString();
    }

    public void setSecondNumber(String secondNumber) {
      this.secondNumber = Long.parseLong(secondNumber);
    }

    public String getSecondNumber() {
      if(secondNumber == null) {
        return null;
      }
      return secondNumber.toString();
    }

    public String getResult() {
      if(result == null) {
        return null;
      }
      return result.toString();
    }

    public void setResult(String result) {
      this.result = Long.parseLong(result);
    }     

    public void sum() {
      this.result = this.firstNumber + this.secondNumber;
    }
}

所以,我有 3 个复合组件都应该做同样的事情,但是当我按下 SUM 按钮时,服务器处理请求后,结果会打印在页面上,但其他 2 个组件被清除他们的价值观。

如何防止这种情况发生?我怎样才能强迫它保留这些价值观?

【问题讨论】:

    标签: jsf-2 composite-component


    【解决方案1】:

    UIComponent 在每次请求时都会重新创建实例,因此每次都会丢失所有实例变量。它们基本上就像请求范围内的托管 bean,而您打算将它们放在视图范围内。您需要在每个属性的基础上考虑视图状态保存。这通常默认情况下已经为#{cc.attrs} 的所有属性完成。所以,如果可以的话,就利用它吧:

    <cc:interface componentType="calculator">
        <cc:attribute name="firstNumber" type="java.lang.Long" />
        <cc:attribute name="secondNumber" type="java.lang.Long" />
    </cc:interface>
    <cc:implementation>
        <h:form>
            <h:inputText id="first" value="#{cc.attrs.firstNumber}" /> 
            <h:commandButton value="+" action="#{cc.sum}"/>
            <h:inputText id="second" value="#{cc.attrs.secondNumber}" /> 
        </h:form>
        <h:outputText id="result" value="#{cc.attrs.result}" />
    </cc:implementation>
    

    仅此(省略空检查;我建议在输入上使用required="true"

    @FacesComponent("calculator")
    public class Calculator extends UINamingContainer {
    
        public void sum() {
            Long firstNumber = (Long) getAttributes().get("firstNumber");
            Long secondNumber = (Long) getAttributes().get("secondNumber");
            getAttributes().put("result", firstNumber + secondNumber);
        }
    
    }
    

    否则,您必须通过将所有属性 getter/setter 委托给 UIComponent#getStateHelper() 来考虑状态保存您自己。基于与您完全相同的 Facelets 代码,整个支持组件将如下所示:

    @FacesComponent("calculator")
    public class Calculator extends UINamingContainer {
    
        public void sum() {
            setResult(getFirstNumber() + getSecondNumber());
        }
    
        public void setFirstNumber(Long firstNumber) {
            getStateHelper().put("firstNumber", firstNumber);
        }
    
        public Long getFirstNumber() {
            return (Long) getStateHelper().eval("firstNumber");
        }
    
        public void setSecondNumber(Long secondNumber) {
            getStateHelper().put("secondNumber", secondNumber);
        }
    
        public Long getSecondNumber() {
            return (Long) getStateHelper().eval("secondNumber");
        }
    
        public void setResult(Long result) {
            getStateHelper().put("result", result);
        }
    
        public Long getResult() {
            return (Long) getStateHelper().eval("result");
        }
    
    }
    

    看,没有局部变量了。请注意,我还通过声明正确的 getter 返回类型和 setter 参数类型,消除了对那些丑陋的手动 String-Long 转换的需要。 JSF/EL 将根据默认转换器或自定义Converters 自动进行转换。由于Long 已经有一个默认值,因此您无需提供自定义Converter


    与具体问题无关,您可以放心地删除getFamily() 方法。 UINamingContainer 已经提供了这一点。如果您正在实现NamingContainer 接口,那么您确实需要自己提供它,但因此这里不是这种情况。上述支持组件示例已将其删除。

    【讨论】:

      猜你喜欢
      • 2011-05-01
      • 2012-12-08
      • 1970-01-01
      • 2011-07-23
      • 1970-01-01
      • 2015-02-05
      • 2020-02-25
      • 2016-07-04
      • 1970-01-01
      相关资源
      最近更新 更多