Подтвердить что ты не робот

Почему f: validateDoubleRange работает только для @SessionScoped?

Может кто-нибудь объяснить мне, почему Foo в моем примере всегда имеет значение null, когда он попадает в класс validateDoubleRange? Конечным результатом является минимальное значение для валидатора всегда 0. Число 3 отображается только на странице, когда в элементе outputText. Он корректно проверяет, если я создаю 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>

Я использую JSF 2 на JBoss 6.1

Если вы там @BalusC, это самый простой пример, который я мог бы придумать, и та же проблема, что и вчера, просто, надеюсь, прояснилась и попросту реплицирована.

- отредактируйте, я должен добавить, что fooId задается с помощью параметра вида, поэтому вам нужно? fooId = 3 в конце вашего URL.

4b9b3361

Ответ 1

Теперь я понимаю, почему я не смог воспроизвести это вчера. В моей игровой среде я случайно использовал следующий параметр контекста, отличный от значения по умолчанию:

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

Эта проблема связана с JSF issue 1492. A <f:validateDoubleRange>, атрибуты которого привязаны к объекту с ограниченным представлением bean, будут неявно воссоздавать весь bean из-за неприятной проблемы с куриным яйцом, потому что валидаторы создаются по запросу и эти свойства передаваться по конструкции валидатора.


Если вы не хотите отключать частичное сохранение состояния (очень разумно), то лучше всего создать и удерживать валидатор в разрешенном управляемом bean себе:

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

(заметьте, что делать это в геттере - плохой дизайн, но поскольку вы готовите foo в сеттере вместо метода прослушивания preRenderView, нет другого пути, иначе это будет сделано в один и тот же метод слушателя)

с

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

Или, альтернативно, создать пользовательский валидатор для того, что заменяет <f:validateDoubleRange>:

<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);
    }

}

Обновление: проблема 1492 куриного яйца фиксируется с Mojarra 2.1.18 (январь 2013 г.). Так что если вы наткнетесь на это в эти дни, тогда вы также можете рассмотреть возможность обновления.