【问题标题】:jsf converter getAsString() always have null as a parameterjsf 转换器 getAsString() 总是有 null 作为参数
【发布时间】:2014-09-30 22:59:45
【问题描述】:

我想创建自己的日期转换器。我有 f:converter id="MyConverter" 的输入字段。当我输入“今天”值时,我想显示今天的日期。方法 getAsObject 运行良好。我从输入字段中获得了“今天”的值并返回新的 LocalDate(),但方法 getAsObject() 始终将 null 作为值。我应该怎么做才能解决它?

    @FacesConverter(forClass = Date.class, value = "MyConverter")
public class MyConverter implements Converter {


    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {

        if (value == null) {
            return null;
        }

        if (value.equals("today")) {
            return new LocalDate();
        }
       //do something else

    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
    // value is always null!
    if (value instanceof LocalDate) {
            LocalDate date = (LocalDate) value;
            return date.toString();
        } else {
            return "";
        }
    }

编辑:

jsf标签(有点复杂)其实是2个日期输入字段(日期间隔组件),我想在上面使用MyDateConverter:

    <cc:interface componentType="DateIntervalComponent">
        <cc:attribute name="value" required="true" />
        <cc:attribute name="placeholder" />
        <cc:attribute name="labelFromDate" default="From Date" />
        <cc:attribute name="labelToDate" default="To Date" />
    </cc:interface>

<cc:implementation>
        <div id="#{cc.userId}">
            <b:input id="fromDate" binding="#{cc.FromDateComponent}"
                label="#{cc.attrs.labelFromDate}">
                <f:converter converterId="MyConverter" />
                <f:ajax render="#{cc.userId}" />
            </b:input>

            <b:input id="toDate" binding="#{cc.ToDateComponent}"
                label="#{cc.attrs.labelToDate}">
                <f:converter converterId="MyConverter" />
                <f:ajax render="#{cc.userId}" />
            </b:input>

        </div>

</cc:implementation>


@FacesComponent("DateIntervalComponent")
public class DateIntervalComponent extends UIInput implements NamingContainer {

    private UIInput fromDateComponent;

    private UIInput toDateComponent;

    public DateIntervalComponent() {
        setRendererType(null);
    }

    @Override
    public String getFamily() {
        return UINamingContainer.COMPONENT_FAMILY;
    }

    @Override
    public DateInterval getValue() {
        return (DateInterval) super.getValue();
    }

    @Override
    public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
        super.processEvent(event);
    }

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        if (fromDateComponent != null && toDateComponent != null) {
            DateInterval value = getValue();
            if (!fromDateComponent.isLocalValueSet()) {
                fromDateComponent.setValue(value != null ? value.getStart() : null);
            }
            if (!toDateComponent.isLocalValueSet()) {
                toDateComponent.setValue(value != null ? value.getEnd() : null);
            }
        }

        super.encodeBegin(context);
    }

    @Override
    public DateInterval getSubmittedValue() {
        Object startValue = fromDateComponent.getValue();
        Object endValue = toDateComponent.getValue();

        return new DateInterval((Date) startValue, (Date) endValue);
    }

    @Override
    public void validate(FacesContext context) {
        super.validate(context);

        if (!isValid()) {
            fromDateComponent.setValid(false);
            toDateComponent.setValid(false);
            return;
        }
    }

    @Override
    public void resetValue() {
        super.resetValue();
        fromDateComponent.resetValue();
        toDateComponent.resetValue();
    }


    public UIInput getFromDateComponent() {
        return fromDateComponent;
    }

    public UIInput getToDateComponent() {
        return toDateComponent;
    }

    public void setToDateComponent(UIInput toDateComponent) {
        this.toDateComponent = toDateComponent;
    }


    public void setFromDateComponent(UIInput fromDateComponent) {
        this.fromDateComponent = fromDateComponent;
    }
}

问候

【问题讨论】:

  • 能否请提供使用转换器的jsf标签页面的解码代码。你如何使用它?没有这个,我们无能为力。
  • 已添加。有任何想法吗?问候
  • 我看到您使用的是无状态的 FacesComponent。这个 cc.FromDateComponent 是如何实现的?您应该将属性的状态处理为 getter 和 setter。您能否提供此 cc.FromDateComponent 的代码??
  • 已添加。 DateIntervalComponent 包含 fromDateComponent 和 toDateComponent(javax.faces.component.UIInput)。所有的 getter 和 setter 也存在。可能是无状态的问题。也许我应该在 MyConverter 中创建 bean(或其他东西),它将捕获 getAsObject 方法中的返回值并在 getAsString 中使用...
  • 这种用于getter 和setter 的方法根本不适用于@FacesComponent。在下面检查我的答案。你甚至不需要声明 private UIInput fromDateComponent

标签: jsf-2 null converter


【解决方案1】:

一旦 FacesComponent 本身是无状态的,您就需要跟踪属性的状态。

我使用以下策略来保持 FacesComponent 属性的状态

首先,创建一个基于 UINamingContainer 的泛型类(如果这是您的组件类型)。

public class MyUINamingContainer extends UINamingContainer {

    @SuppressWarnings("unchecked")
    /**
     * Searches for an attribute and return it.
     */
    protected <T> T getAttribute(String attName) {
        return (T) getStateHelper().eval(attName);
    }

    @SuppressWarnings("unchecked")
    /**
     * Evaluate an attribute and return it if found.
     */
    protected <T> T getAttribute(String localName, String attName) {
        return (T) getStateHelper().eval(localName, getAttributes().get(attName));
    }

    /**
     * Refresh the attributes state
     * 
     * @param <T>
     * @param attName attribute's name. This name will identify the attribute into the map. 
        It must be different from the name used to the attribute on the view, otherwise
        it will cause a infinite loop.
     * @param value attribute's value.
    */
    protected <T> void setAttribute(String attName, T value) {
        getStateHelper().put(attName, value);
    }
}

然后,让我的 FacesComponent 类继承它,这样它就会知道有状态的 getter 和 setter:

@FacesComponent("myComponent")
public class MyComponent extends MyUINamingContainer {

    public SomeType getMyAttributeOnStateMap() {
        return getAttribute("myAttributeOnStateMap","myAttribute");
    }

    public void setMyAttributeOnStateMap(SomeType myAttribute) {
        setAttribute("myAttributeOnStateMap", myAttribute);
    }
}

进入组件xhtml代码:

<cc:interface componentType="myComponent">
    <cc:attribute name="myAttribute"/>
</cc:interface>

<cc:implementation>
    <h:input value="#{cc.myAttributeOnStateMap}" />
<cc:implementation>

PS.:请注意,该属性在地图中具有不同的名称。这是因为它会陷入一个循环,如果您对地图属性和视图使用相同的名称。 getAttribute(String,String) 方法总是会寻找相同的属性,却永远找不到任何东西,导致溢出。

PS2.:请注意,用作 h:input 中的值的名称是用于将其存储到地图中的名称。

【讨论】:

    猜你喜欢
    • 2013-10-05
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 2019-02-16
    • 2015-04-11
    • 2012-03-08
    • 2012-12-26
    • 2012-07-31
    相关资源
    最近更新 更多