PrimeFaces DataTable日历单元格验证:获取无效输入值问题
问题分析
咱们先搞清楚核心问题:PrimeFaces的p:calendar默认用DateTimeConverter,当用户输入无效格式的字符串(比如"123456")时,转换器转换失败,会把绑定的模型值(#{aa.timeFrom}/#{aa.timeTo})设为null,而且不会主动保留用户输入的原始无效值。要实现获取无效值或者区分未输入/无效输入,可以试试下面几个可行的方案:
方案1:直接从请求参数中提取原始输入值
JSF表单提交时,所有输入组件的原始值都会以「组件客户端ID=输入内容」的形式存在请求参数里。你可以在submit方法里遍历请求参数,结合DataTable的行索引定位到具体行的输入值:
public void submit() { FacesContext context = FacesContext.getCurrentInstance(); Map<String, String> requestParams = context.getExternalContext().getRequestParameterMap(); // 假设你的表单ID是form,DataTable ID是dataTable,Calendar组件ID分别是timeFrom、timeTo int rowCount = aa.getYourDataList().size(); // 替换成你的数据列表行数 for (int i = 0; i < rowCount; i++) { // 构造Time From组件的客户端ID(格式:表单ID:DataTableID:行索引:组件ID) String timeFromClientId = String.format("form:dataTable:%d:timeFrom", i); String rawTimeFrom = requestParams.get(timeFromClientId); // 构造Time To组件的客户端ID String timeToClientId = String.format("form:dataTable:%d:timeTo", i); String rawTimeTo = requestParams.get(timeToClientId); // 区分未输入和无效输入 // Time From判断逻辑 if (rawTimeFrom == null || rawTimeFrom.isEmpty()) { System.out.println("行" + i + "的Time From未输入"); } else if (aa.getYourDataList().get(i).getTimeFrom() == null) { System.out.println("行" + i + "的Time From无效输入:" + rawTimeFrom); } // Time To判断逻辑同理 if (rawTimeTo == null || rawTimeTo.isEmpty()) { System.out.println("行" + i + "的Time To未输入"); } else if (aa.getYourDataList().get(i).getTimeTo() == null) { System.out.println("行" + i + "的Time To无效输入:" + rawTimeTo); } } // 后续业务逻辑... }
注意:客户端ID的格式要和页面实际生成的一致,你可以通过浏览器开发者工具查看Calendar元素的
id属性确认。
方案2:自定义转换器保留无效输入值
自定义一个继承自DateTimeConverter的转换器,在转换失败时把原始输入值存储到组件的属性中,这样就能在submit方法里直接从组件获取无效值:
步骤1:实现自定义转换器
@FacesConverter("customDateTimeConverter") public class CustomDateTimeConverter extends DateTimeConverter { @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { try { // 先尝试用默认逻辑转换 return super.getAsObject(context, component, value); } catch (ConverterException e) { // 转换失败时,把原始值存到组件的attributes里 component.getAttributes().put("invalidInput", value); // 继续抛出异常,保留PrimeFaces默认的错误提示 throw e; } } }
步骤2:在Calendar组件中使用自定义转换器
修改xhtml代码,给p:calendar添加转换器:
<p:column headerText="Time From" > <p:cellEditor> <f:facet name="output"> <p:calendar id="timeFrom" pattern="dd/MM/yyyy HH:mm:ss.SSS" value="#{aa.timeFrom}" converter="customDateTimeConverter"/> </f:facet> <f:facet name="input"> <p:calendar id="timeFrom" pattern="dd/MM/yyyy HH:mm:ss.SSS" value="#{aa.timeFrom}" converter="customDateTimeConverter"/> </f:facet> </p:cellEditor> </p:column> <!-- Time To列同理,添加converter="customDateTimeConverter" -->
步骤3:在submit方法中获取无效值
public void submit() { FacesContext context = FacesContext.getCurrentInstance(); // 找到DataTable组件 DataTable dataTable = (DataTable) context.getViewRoot().findComponent("form:dataTable"); for (int i = 0; i < dataTable.getRowCount(); i++) { dataTable.setRowIndex(i); // 获取Time From组件 UIComponent timeFromComp = dataTable.findComponent("timeFrom"); String invalidTimeFrom = (String) timeFromComp.getAttributes().get("invalidInput"); // 获取Time To组件 UIComponent timeToComp = dataTable.findComponent("timeTo"); String invalidTimeTo = (String) timeToComp.getAttributes().get("invalidInput"); // 区分未输入和无效输入 if (invalidTimeFrom != null) { System.out.println("行" + i + "的Time From无效输入:" + invalidTimeFrom); // 用完后清除属性,避免影响下一次提交 timeFromComp.getAttributes().remove("invalidInput"); } else if (aa.getTimeFrom() == null) { System.out.println("行" + i + "的Time From未输入"); } if (invalidTimeTo != null) { System.out.println("行" + i + "的Time To无效输入:" + invalidTimeTo); timeToComp.getAttributes().remove("invalidInput"); } else if (aa.getTimeTo() == null) { System.out.println("行" + i + "的Time To未输入"); } } // 后续业务逻辑... }
方案3:利用JSF错误消息定位无效行
PrimeFaces的Calendar转换失败时,会自动生成带有组件客户端ID的错误消息。你可以在submit方法里遍历这些消息,定位到具体的无效行和字段:
public void submit() { FacesContext context = FacesContext.getCurrentInstance(); // 遍历所有错误消息,定位无效输入的行和字段 for (FacesMessage msg : context.getMessageList()) { if (msg.getSeverity() == FacesMessage.SEVERITY_ERROR) { String clientId = msg.getClientId(); if (clientId != null) { // 解析客户端ID,格式示例:form:dataTable:0:timeFrom String[] parts = clientId.split(":"); if (parts.length >= 4) { int rowIndex = Integer.parseInt(parts[2]); String fieldName = parts[3]; System.out.println("行" + rowIndex + "的" + fieldName + "存在无效输入"); } } } } // 区分未输入:模型值为null且无对应错误消息的就是未输入 for (int i = 0; i < aa.getYourDataList().size(); i++) { YourDataItem item = aa.getYourDataList().get(i); // 判断Time From是否未输入 boolean hasTimeFromError = context.getMessageList("form:dataTable:" + i + ":timeFrom").size() > 0; if (item.getTimeFrom() == null && !hasTimeFromError) { System.out.println("行" + i + "的Time From未输入"); } // 判断Time To是否未输入 boolean hasTimeToError = context.getMessageList("form:dataTable:" + i + ":timeTo").size() > 0; if (item.getTimeTo() == null && !hasTimeToError) { System.out.println("行" + i + "的Time To未输入"); } } // 后续业务逻辑... }
总结
- 如果只是需要快速获取原始无效值,方案1最直接;
- 如果希望更优雅地关联组件和无效值,方案2更合适;
- 如果只需要判断哪些行有无效输入,方案3可以利用现有错误消息,无需额外修改转换器。
内容的提问来源于stack exchange,提问作者amah
相关产品推荐
相关产品推荐

