我使用Prime faces 3.4.1.作为组件框架,使用JSF 2.0作为服务器端框架
以下是我的要求
1) 我有一个标签为"需要会议"的字段。然后我有SelectOneRadio,有两个选项"否"one_answers"是",默认值为"否"。我使用的是JSF/HTML组件h:SelectOneRadio。
2) 我有另一个字段,它是日历组件,这是一个素数面日历组件。当用户选择"是"表示"需要会议"时,用户应从日历控件中选择日期。
3) 如果用户选择"是"但未选择日期,则应显示一条验证消息,指示应选择日期。
我创建了一个自定义验证组件并连接到SelectOneRadio,我可以在自定义验证器中看到选定的值。现在,我尝试从Calendar组件中获取值,以检查该值是否为空,通过UIComponent.getParent().findComponent("rvmDate"),我获取了该组件,但我不知道如何检查日期组件是否为空或包含任何值。
请帮我获取用户选择的日期值。
请帮我解决这个问题。或者还有其他办法吗?请查找源代码。
XHTML页面
<h:selectOneRadio id="rvmMeetingOption"
readonly="#{wipMB.rvmMeetingOptionReadOnly}"
value="#{wipMB.requirementsMeeting}"
disabled="#{wipMB.rvmMeetingOptionDisabled}"
validator="#{wipMB.validateRVMDate}"
validatorMessage="Please enter RVM Date>
<f:selectItem itemLabel="No" itemValue="0"></f:selectItem>
<f:selectItem itemLabel="Yes" itemValue="1" ></f:selectItem>
<f:attribute value="#{rvmDateComp}" name="rvmDateComp"></f:attribute>
</h:selectOneRadio>
<p:calendar id="rvmDate"
readonly="#{wipMB.rvmMeetingDateReadOnly}"
disabled="#{wipMB.rvmMeetingDateDisabled}"
readonlyInput="true"
navigator="true" mode="popup"
pattern="dd/MM/yyyy"
value="#{wipMB.rvmDate}"
effect="explode"
yearRange="1900:2500"
style="margin-left:5px"
binding="#{rvmDateComp}"
</p:calendar>
<p:message id="rvmDateMsg" for="rvmDate" display="both" ></p:message>
自定义验证器
public void validateRVMDate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String invalidDate;
String rvmOption;
Date rvmDate;
String rvmDt = "";
try
{
FacesContext fc = FacesContext.getCurrentInstance();
rvmOption = value.toString();
DateFormat formatter = new SimpleDateFormat("E MMM dd HH:mm:ss Z yyyy");
UIInput rvmCompDt = (UIInput)component.getAttributes().get("rvmDateComp");
rvmDateId = rvmCompDt.getId();
rvmDt = rvmCompDt.getSubmittedValue() == null ? "" : rvmCompDt.getSubmittedValue().toString();
if (rvmOption.equals("1") && rvmDt.isEmpty())
{
FacesMessage msg = new FacesMessage("RVM date is required");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
fc.addMessage("rvmDateMsg", msg);
throw new ValidatorException(msg);
}
}
catch (Exception ex)
{
String msg = ex.getMessage();
}
}
在JSF中,每个组件都是自己的一个小MVC堆栈;有Model(存储为值)、Controller(组件对象)和View(渲染器)。验证器和控制器是体系结构的一部分,用于在模型和视图之间移动值。
虽然JSF验证器扮演着重要的角色,但它只有在这个小小的MVC堆栈中才是重要的。它们并不是为了"验证表单"而设计的,而是为了"验证组件值"而制作的。遗憾的是,"验证器"这个名字让每一个接触JSF的人都认为,每次需要进行任何验证时,验证器就是解决方案。奇怪的是,转换器并没有被滥用。
在您的案例中,构建一个自定义验证器会产生一种奇怪的情况,其中:
- 验证器和视图彼此具有循环依赖性
- 需要使用黑客(例如"立即")和低级API
- 在不太可能的地方硬编码视图逻辑
- 需要更多的知识,而且不稳定。例如,从日历获取值的逻辑可能会有所不同,这取决于单选按钮在文档顺序中是在日历之前还是之后
上面的问题是可以解决的,但由于它们都是由于滥用JSF架构而产生的,我认为最好重新思考这个问题。由于您的验证涉及应用程序流,因此它非常适合操作方法,在该方法中,所有复杂情况都将分解为一个带有条件"addMessage"的简单"if"语句。
您首先需要从<p:calendar>
中删除immediate="true"
,否则在处理单选按钮时根本不会处理它。
然后,要检查字符串是否为null或为空,只需执行
String dt = (String) uiCalendar.getSubmittedValue();
if (dt == null || dt.isEmpty()) {
// dt is null or empty. Throw validator exception depending on the
// current radio button value. Note: you should not catch it yourself!
}
请注意,这与JSF无关。它只是基本的Java。您作为if (dt == "")
的初始尝试确实完全无效。String
是一个对象,而不是基元。==
通过引用而不是通过对象的内部值来比较对象。从技术上讲,您应该使用if (dt.equals(""))
,但isEmpty()
更好。
与具体问题无关更简单的方法是只检查日历组件的required
属性中的单选按钮值。首先通过binding
将单选按钮组件绑定到视图中的一个变量,然后在required
属性中引用其UIInput#getValue()
方法。
<h:selectOneRadio id="rvmMeetingOption" binding="#{rvmMeetingOption}"
readonly="#{wipMB.rvmMeetingOptionReadOnly}"
value="#{wipMB.requirementsMeeting}"
disabled="#{wipMB.rvmMeetingOptionDisabled}">
<f:selectItem itemLabel="No" itemValue="0"></f:selectItem>
<f:selectItem itemLabel="Yes" itemValue="1" ></f:selectItem>
</h:selectOneRadio>
<p:calendar id="rvmDate"
readonly="#{wipMB.rvmMeetingDateReadOnly}"
disabled="#{wipMB.rvmMeetingDateDisabled}"
readonlyInput="true"
navigator="true" mode="popup"
pattern="dd/MM/yyyy"
value="#{wipMB.rvmDate}"
effect="explode"
yearRange="1900:2500"
style="margin-left:5px"
required="#{rvmMeetingOption.value == 1}">
</p:calendar>
使用
UIInput uiCalendar = (UIInput) component.getParent().findComponent("rvmDate");
Date test = uiCalendar.getValue();
if(test==null){
throw ValidatorException
}
然后,测试将填写日期,或者当在日期字段