在 p:dataTable 过滤器中显式使用隐式 JSF 转换器 javax.faces.convert.EnumConverter
Using the implicit JSF converter javax.faces.convert.EnumConverter explicitly in p:dataTable filters
枚举:
public enum OrderStatus {
New("New"),
Paid("Paid"),
Shipped("Shipped"),
Completed("Completed");
private final String label;
private OrderStatus(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
使用 enum
输入 <p:dataTable>
过滤器,如下所示。
<p:dataTable var="row"
value="#{testBacking}"
lazy="true"
rows="10"
widgetVar="dataTableUIWidget">
<p:column id="id" headerText="Id">
<h:outputText value="#{row.orderId}"/>
</p:column>
<p:column headerText="Order Status" filterBy="#{row.orderStatus}">
<f:facet name="filter">
<p:selectOneMenu onchange="PF('dataTableUIWidget').filter();">
<f:selectItem itemLabel="Select" itemValue=""/>
<f:selectItems var="orderStatus"
value="#{enumBean.orderStatus}"
itemLabel="#{orderStatus.label}"/>
</p:selectOneMenu>
</f:facet>
<h:outputText value="#{row.orderStatus}"/>
</p:column>
</p:dataTable>
row.orderStatus
是上述 enum
在其关联的 JPA 实体中的一种类型。
与 <p:selectOneMenu>
关联的过滤器需要明确指定 javax.faces.convert.EnumConverter
,因为它的值未绑定到支持 bean 属性(否则适当的转换器会适当地播放它的角色基于关联的支持 bean 中 属性 的类型隐含地自行发挥作用。
我希望提到转换器如下
<f:converter converterId="javax.faces.Enum"/>
应该像其他隐式转换器一样工作。
但是,这会导致问题(当如上所述指定 <f:converter>
时)。
Severe: JSF1006: Cannot instantiate converter of type javax.faces.Enum
这个转换器有什么问题?我正在寻找一个可行的解决方案。
使用 PrimeFaces 5.2 最终版本/Mojarra 2.2.12。
补充:
converter 指定 javax.faces.Enum
为 converterId
。
public class EnumConverter implements Converter, PartialStateHolder {
public static final String CONVERTER_ID = "javax.faces.Enum";
public static final String ENUM_ID = "javax.faces.converter.EnumConverter.ENUM";
public static final String ENUM_NO_CLASS_ID = "javax.faces.converter.EnumConverter.ENUM_NO_CLASS";
private Class<? extends Enum> targetClass;
private boolean isTransient;
private boolean initialState;
public EnumConverter() {}
}
因此,这应该通过将 javax.faces.Enum
指定为 <f:converter>
的 convertId
作为其他隐式 JSF 转换器来工作。
上述测试用例中使用的 bean(完全可选以供同行评审):
@Named
@ViewScoped
public class TestBacking extends LazyDataModel<OrderTable> implements Serializable {
@Inject
private OrderService orderService;
private static final long serialVersionUID = 1L;
public TestBacking() {}
@Override
public List<OrderTable> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
if (MapUtils.isNotEmpty(filters)) { // Debugging purpose only.
System.out.println("Enum filter : " + (filters.get("orderStatus") instanceof OrderStatus));
System.out.println("Filter value : " + filters.get("orderStatus"));
}
int rowCount = MapUtils.isNotEmpty(filters) ? orderService.rowCount(filters).intValue() : orderService.rowCount().intValue();
setRowCount(rowCount);
return orderService.getList(first, pageSize, null, filters);
}
}
load()
方法中的 stdout
语句显示以下输出。
Enum filter : false
Filter value : New
(filters.get("orderStatus") instanceof OrderStatus
returns false
很明显,因为过滤器组件 <p:selectOneMenu>
没有通过转换器。它只是 returns String
没有转换)。
EnumConverter
是一个特殊的转换器,只能用 Class<E>
作为参数构造(最终设置为 targetClass
)。没有它,转换器将无法工作。不幸的是,它并不是在所有枚举中都可用(即它实际上不是 "generic enum converter")。
在那里你假设 implicit/automatic 枚举转换,它实际上是由 EL 强制转换完成的,而不是由 JSF EnumConverter
完成的。只要目标类型可解析为枚举,EL 确实对枚举具有通用支持。
要显式使用 JSF 枚举转换器,您基本上需要像下面那样扩展 EnumConverter
以将目标枚举传递给 c'tor(无需覆盖 getAsString/Object()
方法):
@FacesConverter(value="orderStatusConverter")
public class OrderStatusConverter extends EnumConverter {
public OrderStatusConverter() {
super(OrderStatus.class);
}
}
然后引用它:
<f:converter converterId="orderStatusConverter" />
不要忘记将 <f:selectItem itemValue="">
更改为 itemValue="#{null}"
,否则您将在 java.lang.String
上获得 ClassCastException
。
如果您碰巧使用了 OmniFaces, you can also use its generic enum converter:
<f:converter converterId="omnifaces.GenericEnumConverter" />
枚举:
public enum OrderStatus {
New("New"),
Paid("Paid"),
Shipped("Shipped"),
Completed("Completed");
private final String label;
private OrderStatus(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
使用 enum
输入 <p:dataTable>
过滤器,如下所示。
<p:dataTable var="row"
value="#{testBacking}"
lazy="true"
rows="10"
widgetVar="dataTableUIWidget">
<p:column id="id" headerText="Id">
<h:outputText value="#{row.orderId}"/>
</p:column>
<p:column headerText="Order Status" filterBy="#{row.orderStatus}">
<f:facet name="filter">
<p:selectOneMenu onchange="PF('dataTableUIWidget').filter();">
<f:selectItem itemLabel="Select" itemValue=""/>
<f:selectItems var="orderStatus"
value="#{enumBean.orderStatus}"
itemLabel="#{orderStatus.label}"/>
</p:selectOneMenu>
</f:facet>
<h:outputText value="#{row.orderStatus}"/>
</p:column>
</p:dataTable>
row.orderStatus
是上述 enum
在其关联的 JPA 实体中的一种类型。
与 <p:selectOneMenu>
关联的过滤器需要明确指定 javax.faces.convert.EnumConverter
,因为它的值未绑定到支持 bean 属性(否则适当的转换器会适当地播放它的角色基于关联的支持 bean 中 属性 的类型隐含地自行发挥作用。
我希望提到转换器如下
<f:converter converterId="javax.faces.Enum"/>
应该像其他隐式转换器一样工作。
但是,这会导致问题(当如上所述指定 <f:converter>
时)。
Severe: JSF1006: Cannot instantiate converter of type javax.faces.Enum
这个转换器有什么问题?我正在寻找一个可行的解决方案。
使用 PrimeFaces 5.2 最终版本/Mojarra 2.2.12。
补充:
converter 指定 javax.faces.Enum
为 converterId
。
public class EnumConverter implements Converter, PartialStateHolder {
public static final String CONVERTER_ID = "javax.faces.Enum";
public static final String ENUM_ID = "javax.faces.converter.EnumConverter.ENUM";
public static final String ENUM_NO_CLASS_ID = "javax.faces.converter.EnumConverter.ENUM_NO_CLASS";
private Class<? extends Enum> targetClass;
private boolean isTransient;
private boolean initialState;
public EnumConverter() {}
}
因此,这应该通过将 javax.faces.Enum
指定为 <f:converter>
的 convertId
作为其他隐式 JSF 转换器来工作。
上述测试用例中使用的 bean(完全可选以供同行评审):
@Named
@ViewScoped
public class TestBacking extends LazyDataModel<OrderTable> implements Serializable {
@Inject
private OrderService orderService;
private static final long serialVersionUID = 1L;
public TestBacking() {}
@Override
public List<OrderTable> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
if (MapUtils.isNotEmpty(filters)) { // Debugging purpose only.
System.out.println("Enum filter : " + (filters.get("orderStatus") instanceof OrderStatus));
System.out.println("Filter value : " + filters.get("orderStatus"));
}
int rowCount = MapUtils.isNotEmpty(filters) ? orderService.rowCount(filters).intValue() : orderService.rowCount().intValue();
setRowCount(rowCount);
return orderService.getList(first, pageSize, null, filters);
}
}
load()
方法中的 stdout
语句显示以下输出。
Enum filter : false
Filter value : New
(filters.get("orderStatus") instanceof OrderStatus
returns false
很明显,因为过滤器组件 <p:selectOneMenu>
没有通过转换器。它只是 returns String
没有转换)。
EnumConverter
是一个特殊的转换器,只能用 Class<E>
作为参数构造(最终设置为 targetClass
)。没有它,转换器将无法工作。不幸的是,它并不是在所有枚举中都可用(即它实际上不是 "generic enum converter")。
在那里你假设 implicit/automatic 枚举转换,它实际上是由 EL 强制转换完成的,而不是由 JSF EnumConverter
完成的。只要目标类型可解析为枚举,EL 确实对枚举具有通用支持。
要显式使用 JSF 枚举转换器,您基本上需要像下面那样扩展 EnumConverter
以将目标枚举传递给 c'tor(无需覆盖 getAsString/Object()
方法):
@FacesConverter(value="orderStatusConverter")
public class OrderStatusConverter extends EnumConverter {
public OrderStatusConverter() {
super(OrderStatus.class);
}
}
然后引用它:
<f:converter converterId="orderStatusConverter" />
不要忘记将 <f:selectItem itemValue="">
更改为 itemValue="#{null}"
,否则您将在 java.lang.String
上获得 ClassCastException
。
如果您碰巧使用了 OmniFaces, you can also use its generic enum converter:
<f:converter converterId="omnifaces.GenericEnumConverter" />