如何为 h:dataTable/ui:repeat 的每一行/项目设置转换器属性?
Posted
技术标签:
【中文标题】如何为 h:dataTable/ui:repeat 的每一行/项目设置转换器属性?【英文标题】:How to set converter properties for each row/item of h:dataTable/ui:repeat? 【发布时间】:2011-11-23 17:44:24 【问题描述】:我创建了一个自定义 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
类中的所有属性只设置一次,然后数据表会根据初始属性呈现和转换值。
我希望属性是按行设置的。我怎样才能做到这一点?
【问题讨论】:
【参考方案1】:到了这一点,您希望每次呈现数据表行时都会设置转换器的属性。这确实不是真的。当要构建视图时,JSF 将只为每个组件创建一个转换器实例,它不会在每次呈现行时创建/重置转换器。
有几种方法可以让它工作。
将动态属性作为组件的<f:attribute>
传递,并让Converter
拦截它。您可以在此处找到示例:JSF convertDateTime with timezone in datatable。然后可以将其用作
<h:outputText value="#item.balanceDate">
<f:converter converterId="isoDateTimeConverter" />
<f:attribute name="pattern" value="#item.pattern" />
</h:outputText>
使用 EL 函数而不是 Converter
。你可以在这里找到一个例子:Facelets and JSTL (Converting a Date to a String for use in a field)。然后可以将其用作
<h:outputText value="#mh:convertIsoDate(item.balanceDate, item.pattern)" />
将转换器和数据表的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
类只是一个具有两个属性 Date date
和 String pattern
的 bean)
这会导致
23-09-2011 2011-09-23 2011 年 9 月 23 日
请改用OmniFaces <o:converter>
。它支持属性中 EL 的渲染时间评估。另见the <o:converter>
showcase example。
<h:outputText value="#item.balanceDate">
<o:converter converterId="isoDateTimeConverter" pattern="#item.pattern" />
</h:outputText>
【讨论】:
我使用f:convertNumber
(与您的f:*
模式匹配)和someCurrencyCode
的动态属性currencyCode="#rowVar.someCurrencyCode"
是实体的属性,rowVar
来自p:dataTable var="rowVar"
。那么我必须在这里编写自己的转换器吗?我已经有了 Primefaces 6.2,如果有帮助的话。或者 Omnifaces 看起来很简单。【参考方案2】:
BalusC 的上述出色(一如既往)回答很全面,但并没有完全达到我的确切要求。就我而言,我需要将Converter
绑定到ui:repeat
中的每个迭代。根据重复的每个项目,我需要不同的Converter
。不过,答案确实为我指明了正确的方向,所以我认为值得分享我的解决方案,以防它对其他人有所帮助。
我使用Converter
将其所有工作委托给属性中指定的另一个Converter
对象,如BalusC 的第一个答案所示。请注意,如果您希望使用带参数的转换器,这根本没有帮助,它针对的是您希望将 Converter
绑定到重复对象的属性的情况。
这是委托Converter
。它也是一个Validator
,其工作方式完全相同。
// package and imports omitted for brevity
@FacesConverter(value="delegatingConverter")
@FacesValidator(value="delegatingValidator")
public class Delegator implements Converter, Validator
// Constants ---------------------------------------------------------------
private static final String CONVERTER_ATTRIBUTE_NAME = "delegateConverter";
private static final String VALIDATOR_ATTRIBUTE_NAME = "delegateValidator";
// Business Methods --------------------------------------------------------
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) throws ConverterException
return retrieveDelegate(component, Converter.class, CONVERTER_ATTRIBUTE_NAME)
.getAsObject(context, component, value);
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value) throws ConverterException
return retrieveDelegate(component, Converter.class, CONVERTER_ATTRIBUTE_NAME)
.getAsString(context, component, value);
@Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException
retrieveDelegate(component, Validator.class, VALIDATOR_ATTRIBUTE_NAME)
.validate(context, component, value);
private <T> T retrieveDelegate(UIComponent component, Class<T> clazz,
String attributeName)
Object delegate = component.getAttributes().get(attributeName);
if (delegate == null)
throw new UnsupportedOperationException("No delegate was specified."
+ " To specify, use an f:attribute tag with: name=\""
+ attributeName + "\"");
if (!(clazz.isAssignableFrom(delegate.getClass())))
throw new UnsupportedOperationException("The specified delegate "
+ "was not a " + clazz.getSimpleName() + " object. " +
"Delegate was: " + delegate.getClass().getName());
return (T) delegate;
所以现在我希望在我的 ui:repeat 中使用此代码,但它不起作用:
<h:outputText value="#item.balanceDate">
<f:converter binding="#item.converter />
<f:validator binding="#item.validator />
</h:outputText>
我可以改用这段代码,它工作正常:
<h:outputText value="#item.balanceDate">
<f:converter converterId="delegatingConverter"/>
<f:validator validatorId="delegatingValidator"/>
<f:attribute name="delegateConverter" value="#item.converter"/>
<f:attribute name="delegateValidator" value="#item.validator"/>
</h:outputText>
假设重复项具有public Converter getConverter()
方法,Validator
类似。
这样做的好处是,在别处使用的相同Converter
s 或Validator
s 可以重复使用而无需任何更改。
【讨论】:
以上是关于如何为 h:dataTable/ui:repeat 的每一行/项目设置转换器属性?的主要内容,如果未能解决你的问题,请参考以下文章