如何为 h:dataTable/ui:repeat 的每一行/项目设置转换器属性? [英] How to set converter properties for each row/item of h:dataTable/ui:repeat?

查看:28
本文介绍了如何为 h:dataTable/ui:repeat 的每一行/项目设置转换器属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个自定义的 ISO 日期时间 Converter:

public class IsoDateTimeConverter 实现了 Converter,StateHolder {私有类类型;私有字符串模式;私有布尔瞬态值 = 假;公共无效集类型(类类型){this.type = 类型;}公共无效setPattern(字符串模式){this.pattern = 模式;}@覆盖public Object getAsObject(FacesContext context, UIComponent component, String value) 抛出 ConverterException {如果(StringCheck.isNullOrEmpty(值)){throw new ConverterException("未指定值");}尝试 {如果(IsoDate.class.equals(类型)){如果(WebConst.ISO_DATE_NONE.equals(值)){返回 IsoDate.DUMMY;} 别的 {//TODO User spezifische TimeZone auslesen返回新的 IsoDate(value, TimeZone.getDefault().getID());}} else if (IsoTime.class.equals(type)) {如果(WebConst.ISO_TIME_NONE.equals(值)){返回 IsoTime.DUMMY;} 别的 {//TODO User spezifische TimeZone auslesen返回新的 IsoTime(value, TimeZone.getDefault().getID());}} else if (IsoTimestamp.class.equals(type)) {如果(WebConst.ISO_TIMESTAMP_NONE.equals(值)){返回 IsoTimestamp.DUMMY;} 别的 {//TODO User spezifische TimeZone auslesen返回新的 IsoTimestamp(value, TimeZone.getDefault().getID());}} 别的 {throw new ConverterException("值不可转换");}} 捕获(异常 e){throw new ConverterException(e.getMessage());}}@覆盖public String getAsString(FacesContext context, UIComponent component, Object value) 抛出 ConverterException {如果(值 == 空){throw new ConverterException("未指定值");}如果 (IsoDate.class.equals(value)) {IsoDate isoDate = (IsoDate) 值;如果(isoDate.isDummy()){返回 WebConst.ISO_DATE_NONE;} 别的 {//TODO User spezifische TimeZone auslesen返回 isoDate.toString(pattern, TimeZone.getDefault().getID(), false);}} else if (IsoTime.class.equals(value)) {IsoTime isoTime = (IsoTime) 值;如果(isoTime.isDummy()){返回 WebConst.ISO_TIME_NONE;} 别的 {//TODO User spezifische TimeZone auslesen返回 isoTime.toString(pattern, TimeZone.getDefault().getID(), false);}} else if (IsoTimestamp.class.equals(value)) {IsoTimestamp isoTimestamp = (IsoTimestamp) 值;如果(isoTimestamp.isDummy()){返回 WebConst.ISO_TIMESTAMP_NONE;} 别的 {//TODO User spezifische TimeZone auslesenreturn isoTimestamp.toString(pattern, TimeZone.getDefault().getID(), false);}} 别的 {throw new ConverterException("值不可转换");}}@覆盖公共对象 saveState(FacesContext 上下文){返回新对象[]{类型,模式};}@覆盖public void restoreState(FacesContext上下文,对象状态){type = (Class) ((Object[]) state)[0];模式=(字符串)((对象[])状态)[1];}@覆盖公共布尔 isTransient() {返回瞬态值;}@覆盖公共无效setTransient(布尔瞬态值){this.transientValue = 瞬态值;}}

我在以下视图中使用 Converter 作为 :

<p:列><h:outputText value="#{item.balanceDate}"immediate="true"><mh:IsoDateTimeConverter type="#{webConst.ISO_DATE_CLASS}" pattern="#{webConst.ISO_DATE_FORMAT}"/></h:outputText></p:列></p:dataTable>

问题是,当我第一次打开这个视图时,所有属性只在我的 Converter 类中设置一次,然后数据表根据初始属性呈现和转换值.

我希望属性是按行设置的.我怎样才能做到这一点?

解决方案

到目前为止,您希望每次呈现数据表行时都会设置转换器的属性.这确实不是真的.在构建视图时,JSF 将只为每个组件创建一个转换器实例,它不会在每次呈现行时创建/重置转换器.

有几种方法可以让它工作.

  • 将动态属性作为组件的 传递,并让 Converter 拦截它.你可以在这里找到一个例子:JSF convertDateTime with timezone in datatable.这可以用作

    <f:converter converterId="isoDateTimeConverter"/><f:attribute name="pattern" value="#{item.pattern}"/></h:outputText>


  • 使用 EL 函数代替 Converter.你可以在这里找到一个例子:Facelets 和 JSTL(将日期转换为用于字段的字符串).这可以用作

    <h:outputText value="#{mh:convertIsoDate(item.balanceDate, item.pattern)}"/>


  • 将转换器和数据表的 DataModel 绑定为同一托管 bean 的属性.通过这种方式,您将能够在返回之前根据行数据设置转换器的属性.这是一个基于标准 JSF 组件和标准 DateTimeConverter 的基本启动示例(它应该在 PrimeFaces 组件和您的自定义转换器上同样有​​效):

    <h:列><h:outputText value="#{item.date}" converter="#{bean.converter}"/></h:列></h:dataTable>

    @ManagedBean@ViewScoped公共类 Bean 实现了 Serializable {私人列表<项目>项目;私有数据模型模型;私有 DateTimeConverter 转换器;@PostConstruct公共无效初始化(){项目 = Arrays.asList(新项目(新日期(),dd-MM-yyyy"),新项目(新日期(),yyyy-MM-dd"),新项目(新日期(),MM/dd/yyyy"));模型 = 新的 ListDataModel(items);转换器 = 新的 DateTimeConverter();}公共数据模型获取模型(){退货模式;}公共转换器 getConverter() {转换器.setPattern(model.getRowData().getPattern());返回转换器;}}

    (Item 类只是一个 bean,具有两个属性 Date dateString pattern)

    结果是

    <块引用>

    23-09-2011
    2011-09-23
    09/23/2011


  • 改用 OmniFaces .它支持属性中 EL 的渲染时间评估.另请参阅 展示示例.

    <o:converter converterId="isoDateTimeConverter" pattern="#{item.pattern}"/></h:outputText>

I have created a custom ISO date time Converter:

public class IsoDateTimeConverter implements Converter, StateHolder {

    private Class type;
    private String pattern;

    private boolean transientValue = false;

    public void setType(Class type) {
        this.type = type;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
        if (StringCheck.isNullOrEmpty(value)) {
            throw new ConverterException("value not specified");
        }

        try {
            if (IsoDate.class.equals(type)) {

                if (WebConst.ISO_DATE_NONE.equals(value)) {
                    return IsoDate.DUMMY;
                } else {
                    //TODO User spezifische TimeZone auslesen
                    return new IsoDate(value, TimeZone.getDefault().getID());
                }

            } else if (IsoTime.class.equals(type)) {

                if (WebConst.ISO_TIME_NONE.equals(value)) {
                    return IsoTime.DUMMY;
                } else {
                    //TODO User spezifische TimeZone auslesen
                    return new IsoTime(value, TimeZone.getDefault().getID());
                }

            } else if (IsoTimestamp.class.equals(type)) {

                if (WebConst.ISO_TIMESTAMP_NONE.equals(value)) {
                    return IsoTimestamp.DUMMY;
                } else {
                    //TODO User spezifische TimeZone auslesen
                    return new IsoTimestamp(value, TimeZone.getDefault().getID());
                }

            } else {
                throw new ConverterException("value not convertible");
            }
        } catch (Exception e) {
            throw new ConverterException(e.getMessage());
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
        if (value == null) {
            throw new ConverterException("value not specified");
        }

        if (IsoDate.class.equals(value)) {
            IsoDate isoDate = (IsoDate) value;

            if (isoDate.isDummy()) {
                return WebConst.ISO_DATE_NONE;
            } else {
                //TODO User spezifische TimeZone auslesen
                return isoDate.toString(pattern, TimeZone.getDefault().getID(), false);
            }

        } else if (IsoTime.class.equals(value)) {
            IsoTime isoTime = (IsoTime) value;

            if (isoTime.isDummy()) {
                return WebConst.ISO_TIME_NONE;
            } else {
                //TODO User spezifische TimeZone auslesen
                return isoTime.toString(pattern, TimeZone.getDefault().getID(), false);
            }

        } else if (IsoTimestamp.class.equals(value)) {
            IsoTimestamp isoTimestamp = (IsoTimestamp) value;

            if (isoTimestamp.isDummy()) {
                return WebConst.ISO_TIMESTAMP_NONE;
            } else {
                //TODO User spezifische TimeZone auslesen
                return isoTimestamp.toString(pattern, TimeZone.getDefault().getID(), false);
            }

        } else {
            throw new ConverterException("value not convertible");
        }
    }

    @Override
    public Object saveState(FacesContext context) {
        return new Object[]{type, pattern};
    }

    @Override
    public void restoreState(FacesContext context, Object state) {
        type = (Class) ((Object[]) state)[0];
        pattern = (String) ((Object[]) state)[1];
    }

    @Override
    public boolean isTransient() {
        return transientValue;
    }

    @Override
    public void setTransient(boolean transientValue) {
        this.transientValue = transientValue;
    }
}

And I use the Converter as <mh:IsoDateTimeConverter> in the following view:

<p:dataTable value="#{imports.list}" var="item">
    <p:column>
        <h:outputText value="#{item.balanceDate}" immediate="true">
            <mh:IsoDateTimeConverter type="#{webConst.ISO_DATE_CLASS}" pattern="#{webConst.ISO_DATE_FORMAT}"/>
        </h:outputText>
    </p:column>
</p:dataTable>

The problem is, when I first open this view, all properties are set in my Converter class only once and then the datatable renders and converts the values based on initial properties.

I expected that the properties are set on a per-row basis. How can I achieve this?

解决方案

To the point, you expected that the converter's properties are set every time a datatable row is rendered. This is indeed not true. JSF will create only one converter instance per component when the view is to be built, it will not create/reset the converter each time the row is rendered.

There are several ways to get it to work.

  • Pass the dynamic attributes as <f:attribute> of the component and let the Converter intercept on that. You can find an example here: JSF convertDateTime with timezone in datatable. This can then be used as

    <h:outputText value="#{item.balanceDate}">
        <f:converter converterId="isoDateTimeConverter" />
        <f:attribute name="pattern" value="#{item.pattern}" />
    </h:outputText>
    


  • Use an EL function instead of a Converter. You can find an example here: Facelets and JSTL (Converting a Date to a String for use in a field). This can then be used as

    <h:outputText value="#{mh:convertIsoDate(item.balanceDate, item.pattern)}" />
    


  • Bind the converter and datatable's DataModel as a property of the same managed bean. This way you will be able to set the converter's properties based on the row data before returning it. Here's a basic kickoff example based on standard JSF components and standard DateTimeConverter (it should work equally good on PrimeFaces components and with your custom converter):

    <h:dataTable value="#{bean.model}" var="item">
        <h:column>
            <h:outputText value="#{item.date}" converter="#{bean.converter}" />
        </h:column>
    </h:dataTable>
    

    with

    @ManagedBean
    @ViewScoped
    public class Bean implements Serializable {
    
        private List<Item> items;
        private DataModel<Item> model;
        private DateTimeConverter converter;
    
        @PostConstruct
        public void init() {
            items = Arrays.asList(
                new Item(new Date(), "dd-MM-yyyy"), 
                new Item(new Date(), "yyyy-MM-dd"), 
                new Item(new Date(), "MM/dd/yyyy"));
            model = new ListDataModel<Item>(items);
            converter = new DateTimeConverter();
        }
    
        public DataModel<Item> getModel() {
            return model;
        }
    
        public Converter getConverter() {
            converter.setPattern(model.getRowData().getPattern());
            return converter;
        }
    
    }
    

    (the Item class is just a bean with two properties Date date and String pattern)

    this results in

    23-09-2011
    2011-09-23
    09/23/2011


  • Use OmniFaces <o:converter> instead. It supports render time evaluation of EL in the attributes. See also the <o:converter> showcase example.

    <h:outputText value="#{item.balanceDate}">
        <o:converter converterId="isoDateTimeConverter" pattern="#{item.pattern}" />
    </h:outputText>
    

这篇关于如何为 h:dataTable/ui:repeat 的每一行/项目设置转换器属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆