为什么 f:validateDoubleRange 仅适用于 @SessionScoped?

发布于 2024-12-05 08:26:30 字数 3284 浏览 0 评论 0原文

有人可以向我解释为什么我的示例中的 Foo 在到达 validateDoubleRange 类时始终为 null 吗?最终结果是验证器的最小值始终为 0。当位于 outputText 元素中时,数字 3 在页面上显示得很好。 如果我使 bean @SessionScoped 而不是 @ViewScoped

控制器:

import java.io.Serializable;
import java.math.BigDecimal;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ViewScoped
@ManagedBean(name = "fooController")
public class FooController implements Serializable {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FooController.class);
    private static final long serialVersionUID = 1L;
    private Foo foo;
    private BigDecimal amount;
    private Long fooId;

    public Long getFooId() {
        return fooId;
    }

    public void setFooId(Long fooId) {
        this.fooId = fooId;
        this.foo = new Foo();
        foo.setFooId(fooId);
        foo.setMinAmount(Double.valueOf(3));
        foo.setMaxAmount(Double.valueOf(10));
    }

    public Foo getFoo() {
        return foo;
    }

    public void sendAmount() {
        log.debug("sendAmount: " + amount);
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public static class Foo {

        private Long fooId;
        private Double minAmount;
        private Double maxAmount;

        public Foo() {
        }

        public void setFooId(Long fooId) {
            this.fooId = fooId;
        }

        public void setMinAmount(Double minAmount) {
            this.minAmount = minAmount;
        }

        public void setMaxAmount(Double maxAmount) {
            this.maxAmount = maxAmount;
        }

        public Long getFooId() {
            return fooId;
        }

        public Double getMaxAmount() {
            return maxAmount;
        }

        public Double getMinAmount() {
            return minAmount;
        }
    }
}

JSP:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:f="http://java.sun.com/jsf/core"
            xmlns:h="http://java.sun.com/jsf/html"
            >
<f:metadata>
    <f:viewParam name="fooId" value="#{fooController.fooId}" />        
</f:metadata>
<h:form id="myForm">
    <h:outputText value="This is correctly displayed: '#{fooController.foo.minAmount}'"/><br/>
    <h:outputText value="My Input:" />
    <h:inputText id="myInput"
                 value="#{fooController.amount}" 
                 required="true"
                 >
        <f:convertNumber maxFractionDigits="2"/>
        <f:validateDoubleRange minimum="#{fooController.foo.minAmount}" maximum="80"/>
    </h:inputText>
    <h:message for="myInput"/>
    <br/>
    <h:commandButton id="myButton"
                     value="Save Amount"
                     action="#{fooController.sendAmount}"
                     >
    </h:commandButton>
</h:form>
</ui:composition>

我在 JBoss 6.1 上使用 JSF 2

如果您在那里@BalusC,那么它 验证正常这是我能想到的最简单的例子,与昨天的问题相同,只是我希望能够澄清并且更容易复制。

-- 编辑,我应该添加 fooId 是通过视图参数设置的,因此您需要在 URL 末尾添加 ?fooId=3 。

Can someone explain to me why Foo in my example is always null when it gets to the validateDoubleRange class? The end result is the min value for the validator is always 0. The number 3 displays just fine on the page when in the outputText element.
It validates fine if I make the bean @SessionScoped instead of @ViewScoped

Controller:

import java.io.Serializable;
import java.math.BigDecimal;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ViewScoped
@ManagedBean(name = "fooController")
public class FooController implements Serializable {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FooController.class);
    private static final long serialVersionUID = 1L;
    private Foo foo;
    private BigDecimal amount;
    private Long fooId;

    public Long getFooId() {
        return fooId;
    }

    public void setFooId(Long fooId) {
        this.fooId = fooId;
        this.foo = new Foo();
        foo.setFooId(fooId);
        foo.setMinAmount(Double.valueOf(3));
        foo.setMaxAmount(Double.valueOf(10));
    }

    public Foo getFoo() {
        return foo;
    }

    public void sendAmount() {
        log.debug("sendAmount: " + amount);
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public static class Foo {

        private Long fooId;
        private Double minAmount;
        private Double maxAmount;

        public Foo() {
        }

        public void setFooId(Long fooId) {
            this.fooId = fooId;
        }

        public void setMinAmount(Double minAmount) {
            this.minAmount = minAmount;
        }

        public void setMaxAmount(Double maxAmount) {
            this.maxAmount = maxAmount;
        }

        public Long getFooId() {
            return fooId;
        }

        public Double getMaxAmount() {
            return maxAmount;
        }

        public Double getMinAmount() {
            return minAmount;
        }
    }
}

JSP:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:f="http://java.sun.com/jsf/core"
            xmlns:h="http://java.sun.com/jsf/html"
            >
<f:metadata>
    <f:viewParam name="fooId" value="#{fooController.fooId}" />        
</f:metadata>
<h:form id="myForm">
    <h:outputText value="This is correctly displayed: '#{fooController.foo.minAmount}'"/><br/>
    <h:outputText value="My Input:" />
    <h:inputText id="myInput"
                 value="#{fooController.amount}" 
                 required="true"
                 >
        <f:convertNumber maxFractionDigits="2"/>
        <f:validateDoubleRange minimum="#{fooController.foo.minAmount}" maximum="80"/>
    </h:inputText>
    <h:message for="myInput"/>
    <br/>
    <h:commandButton id="myButton"
                     value="Save Amount"
                     action="#{fooController.sendAmount}"
                     >
    </h:commandButton>
</h:form>
</ui:composition>

I am using JSF 2 on JBoss 6.1

If you are out there @BalusC, this is the simplest example I could come up with, and is the same problem as yesterday, just, I hope, clarified and simpler to replicate.

-- edit, I should add that fooId is set via a view param, so you need ?fooId=3 at the end of your URL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夏の忆 2024-12-12 08:26:30

我现在明白为什么我昨天无法重现这个了。在我的游乐场环境中,我意外地将以下上下文参数设置为与默认值不同:

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

此问题与 JSF 问题 1492。其属性通过 EL 绑定到视图作用域 bean 属性的 将由于令人讨厌的先有鸡还是先有蛋的问题而隐式重新创建整个 bean,因为验证器是根据每个请求创建的基础,这些属性将在验证器的构造中传递。


如果您不想禁用部分状态保存(非常合理),那么您最好的选择是自己在视图范围的托管 bean 中创建并保存验证器:

public Validator getValidator() {
    return new DoubleRangeValidator(foo.getMaximum(), foo.getMinimum());
}

(注意,在 getter 中执行此操作是一个糟糕的设计,但由于您是在 setter 中准备 foo 而不是 preRenderView 侦听器方法,因此没有其他方法,否则它将在相同的侦听器方法中完成)< /em>

<h:inputText validator="#{fooController.validator.validate}" ...>

Or,或者, 更新替换 ::

<f:validator validatorId="bindableDoubleRangeValidator" />
<f:attribute name="minimum" value="#{fooController.foo.minAmount}" />
<f:attribute name="maximum" value="#{fooController.foo.maxAmount}" />

创建一个自定义验证器,用

package com.example;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.DoubleRangeValidator;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.ValidatorException;

@FacesValidator("bindableDoubleRangeValidator")
public class BindableDoubleRangeValidator extends DoubleRangeValidator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        setMinimum((Double) component.getAttributes().get("minimum"));
        setMaximum((Double) component.getAttributes().get("maximum"));
        super.validate(context, component, value);
    }

}

自 Mojarra 2.1.18(2013 年 1 月)以来,先有鸡还是先有蛋的问题 1492 已得到修复。因此,如果您最近偶然发现这一点,那么您也可以考虑升级。

I now see why I couldn't reproduce this yesterday. In my playground environment I had accidentally the following context param set differently from the default value:

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

This problem is related to JSF issue 1492. A <f:validateDoubleRange> whose attributes are by EL bound to a view scoped bean property will implicitly recreate the whole bean due to a nasty chicken-egg issue, because validators are created on a per-request basis and those properties are to be passed on validator's construction.


If you do not want to disable partial state saving (very reasonable), then your best bet is to create and hold the validator in the view scoped managed bean yourself:

public Validator getValidator() {
    return new DoubleRangeValidator(foo.getMaximum(), foo.getMinimum());
}

(note, doing this stuff in a getter is a bad design, but since you're preparing foo in a setter instead of a preRenderView listener method, there's no other way, otherwise it would be done right in the same listener method)

with

<h:inputText validator="#{fooController.validator.validate}" ...>

Or, alternatively, to create a custom validator for that which replaces the <f:validateDoubleRange>:

<f:validator validatorId="bindableDoubleRangeValidator" />
<f:attribute name="minimum" value="#{fooController.foo.minAmount}" />
<f:attribute name="maximum" value="#{fooController.foo.maxAmount}" />

with

package com.example;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.DoubleRangeValidator;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.ValidatorException;

@FacesValidator("bindableDoubleRangeValidator")
public class BindableDoubleRangeValidator extends DoubleRangeValidator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        setMinimum((Double) component.getAttributes().get("minimum"));
        setMaximum((Double) component.getAttributes().get("maximum"));
        super.validate(context, component, value);
    }

}

Update: the chicken-egg issue 1492 is fixed since Mojarra 2.1.18 (January 2013). So if you stumble upon this these days, then you could also consider to just upgrade.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文