如何为 h:dataTable/ui:repeat 的每行/项目设置转换器属性?
我创建了一个自定义 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
作为
:
<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
类中设置一次,然后数据表根据初始属性呈现并转换值。
我预计属性是按行设置的。我怎样才能实现这个目标?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
到目前为止,您期望每次呈现数据表行时都会设置转换器的属性。这确实不是真的。当要构建视图时,JSF 将仅为每个组件创建一个转换器实例,它不会在每次呈现行时创建/重置转换器。
有几种方法可以让它发挥作用。
将动态属性作为组件的
传递,并让Converter
拦截该属性。您可以在这里找到一个示例:JSF ConvertDateTime with timezone in datatable。然后可以将其用作使用 EL 函数而不是
转换器
。您可以在此处找到示例: Facelets 和 JSTL(将日期转换为字符串以在字段中使用)。然后可以将其用作绑定转换器和数据表的
DataModel
作为同一托管 bean 的属性。这样,您将能够在返回行数据之前设置转换器的属性。下面是一个基于标准 JSF 组件和标准DateTimeConverter
的基本启动示例(它在 PrimeFaces 组件和自定义转换器上应该同样有效):与
<前><代码>@ManagedBean
@ViewScoped
公共类 Bean 实现可序列化 {
私有列表<项目>项目;- 模型;
私有数据模型
私有 DateTimeConverter 转换器;
@PostConstruct- (items);
公共无效初始化(){
项目 = Arrays.asList(
新项目(新日期(),“dd-MM-yyyy”),
新项目(新日期(),“yyyy-MM-dd”),
新项目(新日期(),“月/日/年”));
模型 = new ListDataModel
转换器=新的DateTimeConverter();
}
公共数据模型- 获取模型(){
返回模型;
}
公共转换器 getConverter() {
converter.setPattern(model.getRowData().getPattern());
返回转换器;
}
}
(
Item
类只是一个具有两个属性Date date
和String pattern
的 bean)这会导致
<块引用>
2011年9月23日
2011-09-23
09/23/2011
使用 OmniFaces
代替。它支持属性中EL的渲染时间评估。另请参阅
展示示例。< /p>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 theConverter
intercept on that. You can find an example here: JSF convertDateTime with timezone in datatable. This can then be used asUse 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 asBind 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 standardDateTimeConverter
(it should work equally good on PrimeFaces components and with your custom converter):with
(the
Item
class is just a bean with two propertiesDate date
andString pattern
)this results in
Use OmniFaces
<o:converter>
instead. It supports render time evaluation of EL in the attributes. See also the<o:converter>
showcase example.BalusC 的上述优秀(一如既往)答案很全面,但并没有完全满足我的确切要求。就我而言,我需要将
Converter
绑定到ui:repeat
中的每次迭代。根据重复的每个项目,我需要不同的Converter
。不过,答案确实为我指明了正确的方向,所以我认为值得分享我的解决方案,以防它对其他人有帮助。我使用一个
Converter
将所有工作委托给属性中指定的另一个Converter
对象,如 BalusC 的第一个答案中所示。请注意,如果您希望使用带参数的转换器,这根本没有帮助,它针对的是您希望将Converter
绑定到重复对象的属性的情况。这是委托
Converter
。它也是一个Validator
,其工作方式完全相同。所以现在我希望在我的 ui:repeat 中使用这段代码,这不起作用:
我可以改用这段代码,它工作正常:
假设重复项有一个
public Converter getConverter() 方法与
Validator
类似。这样做的优点是,在其他地方使用的相同的
Converter
或Validator
可以重复使用而无需任何更改。The above excellent (as always) answer from BalusC is comprehensive but didn't quite hit my exact requirement. In my case, I need to bind a
Converter
to each iteration in aui:repeat
. I need a differentConverter
depending on each item being repeated. The answer did point me in the right direction, though, so I thought it worth sharing my solution in case it helps anyone else.I use a
Converter
that delegates all it's work to anotherConverter
object specified in the attribute, as in the first of BalusC's answers. Note that this doesn't help at all if you wish to use converters with parameters, it's aimed at the situation where you would want to bind aConverter
to a property of a repeating object.Here's the delegating
Converter
. It's also aValidator
, which works in exactly the same way.So now where I would wish to use this code within my ui:repeat, which won't work:
I can instead use this code, which works OK:
Assuming that the repeating item has a
public Converter getConverter()
method and similar for theValidator
.This does have the advantage that the same
Converter
s orValidator
s that are used elsewhere can be re-used without any changes.