Как настроить свойства конвертера для каждой строки данных?

Я создал пользовательское время даты ISO 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;
    }
}

И я использую Converter как <mh:IsoDateTimeConverter> в следующем представлении:

<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>

Проблема заключается в том, что когда я впервые открываю это представление, все свойства устанавливаются в моем классе Converter только один раз, а затем datatable визуализирует и преобразует значения на основе начальных свойств.

Я ожидал, что свойства заданы для каждой строки. Как я могу достичь этого?

Ответ 1

К моменту, вы ожидали, что свойства конвертера задаются каждый раз, когда создается строка данных. Это действительно не так. JSF создаст только один экземпляр преобразователя для каждого компонента при создании представления, он не будет создавать / reset конвертер при каждом рендеринге строки.

Существует несколько способов заставить его работать.

  • Передайте динамические атрибуты как <f:attribute> компонента и перехватите Converter. Здесь вы можете найти пример: JSF convertDateTime с часовым поясом в datatable. Это можно затем использовать как

    <h:outputText value="#{item.balanceDate}">
        <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)}" />
    

  • Привяжите конвертер и datatable DataModel как свойство того же управляемого bean. Таким образом вы сможете установить свойства конвертера на основе данных строки перед его возвратом. Здесь приведен пример базового запуска, основанный на стандартных компонентах JSF и стандартном DateTimeConverter (он должен хорошо работать на компонентах PrimeFaces и с вашим пользовательским конвертером):

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

    с

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

    (класс Item - это просто bean с двумя свойствами Date date и String pattern)

    это приводит к

    23.09.2011
    2011-09-23
    09/23/2011


  • Используйте OmniFaces <o:converter>. Он поддерживает оценку времени выполнения EL в атрибутах. См. Также пример демонстрации <o:converter>.

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