【问题标题】:Using JSF Validator使用 JSF 验证器
【发布时间】:2013-01-23 23:08:39
【问题描述】:

我使用 Prime faces 3.4.1 作为组件框架,JSF 2.0 作为服务器端框架

以下是我的要求

1) 我有一个标签为“需要开会”的字段。然后我有 SelectOneRadio 有两个选项“否”和“是”,默认值为“否”。我正在使用 JSF/HTML 组件 h:SelectOneRadio。

2) 我有另一个字段是日历组件,这是一个 primefaces 日历组件。当用户选择“是”表示“需要开会”时,用户应该从日历控件中选择一个日期。

3) 如果用户选择“是”但未选择日期,则应显示一条验证消息,指示应选择日期。

我创建了一个自定义验证组件并附加到 SelectOneRadio,我能够在自定义验证器中看到选定的值。现在,我尝试从 Calendar 组件中获取值以检查该值是否为空,通过 UIComponent.getParent().findCompoent("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 validation


    【解决方案1】:

    在 JSF 中,每个组件都是自己的一个小 MVC 堆栈;有模型(存储为值)、控制器(组件对象)和视图(渲染器)。验证器和控制器是架构的一部分,需要在模型和视图之间移动值。

    虽然 JSF 验证器发挥着重要作用,但它只在这个小的 MVC 堆栈内部很重要。它们不是为了“验证表单”而设计的,它们是为了“验证组件价值”而严格制作的。可悲的是,“验证器”这个名字让每个来到 JSF 的人都认为,每次需要进行任何验证时,验证器就是解决方案。奇怪的是,转换器并没有被如此滥用。

    在您的情况下,构建自定义验证器会产生一种奇怪的情况,其中:

    • 验证器和视图之间存在循环依赖关系,
    • 需要使用 hack(例如“立即”)和低级 API,
    • 在不太可能的地方硬编码视图逻辑,
    • 需要更多的知识,而且很不稳定。例如,根据单选按钮在文档顺序中是在日历之前还是之后,从日历中获取值的逻辑可能会有所不同。

    上面的问题是可以解决的,但是由于都是滥用JSF架构造成的,我觉得还是重新思考问题比较好。由于您的验证涉及应用程序流程,因此它非常适合 action 方法,其中所有复杂性都将分解为带有条件“addMessage”的单个简单“if”语句。

    【讨论】:

      【解决方案2】:

      首先需要将immediate="true"&lt;p:calendar&gt;中去掉,否则处理单选按钮时根本不处理。

      然后,要检查一个字符串是否为空或空,只需这样做

      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>
      

      【讨论】:

        【解决方案3】:

        使用

        UIInput uiCalendar = (UIInput) component.getParent().findComponent("rvmDate");
        Date test = uiCalendar.getValue();
        if(test==null){
        throw ValidatorException
        }
        

        test 将填写日期,或者在日期字段中未选择任何内容时为 null

        【讨论】:

        • 组件按照它们在树中出现的顺序进行处理。层次结构中下一个组件的getValue() 在当前组件中不可用。你需要getSubmittedValue()
        • 我对此进行了测试,它给了我所选日期的值。 getSubmittedValue() 总是给我 null。
        • 在 OP 的情况下,日历有 immediate="true" 这并不完全正确。
        • 感谢大家的投入。通常,我将支持 bean 中的绑定属性关联为一个属性。但我没有看到对支持 bean 的引用,我只看到一个 binding="#{rvmMeetingOption}"。
        • 非常感谢 BaluC 的回复。我想提以下几点。我浏览了您的一篇文章“多个字段的验证器”,并按照您在文章中的建议实现了验证器,方法是将 f:attribute 添加到 selectoneradio 并将相应的绑定添加到日历。现在我能够得到这些值。但是即使我抛出验证器异常,验证器消息也不会显示。此外,验证器异常将被忽略并提交页面。
        猜你喜欢
        • 1970-01-01
        • 2013-07-25
        • 2011-04-28
        • 2018-01-28
        • 1970-01-01
        • 2011-11-18
        • 1970-01-01
        • 1970-01-01
        • 2012-05-14
        相关资源
        最近更新 更多