【问题标题】:JSF 2.0: How to skip JSR-303 bean validation?JSF 2.0:如何跳过 JSR-303 bean 验证?
【发布时间】:2011-06-18 15:48:57
【问题描述】:

单击按钮时如何使用 JSF 跳过 JSR-303 Bean 验证?

一个有点长的问题来解释一些方法......考虑一个表格中的列表:

<h:form id="form">
    <h:commandButton value="Add row">
        <f:ajax execute="foo" listener="#{bean.add()}" render="foo" />
    </h:commandButton>
    <h:dataTable id="foo" var="foo" value="#{bean.foos}">
        <h:column>
            Name: <h:inputText id="field" value="#{foo.name}" required="true" />
            <h:messages for="field" />
        </h:column>
        <h:column>
            <h:commandButton value="Remove">
                <f:ajax execute=":form:foo" listener="#{bean.remove(foo)}" render=":form:foo" />
            </h:commandButton>
        </h:column>
    </h:dataTable>
</h:form>

当用户单击添加或删除行时,该操作应在未经验证的情况下执行。问题是,JSF 重新呈现整个列表并尝试验证它。如果有未验证的草稿更改,则会发生验证错误,并且永远不会调用侦听器方法(因为验证失败会阻止这种情况发生)。但是,将immediate="true" 添加到 f:ajax 允许方法执行,尽管存在验证错误。但是,仍然会出现验证错误,并在此处显示。

我看到两个选项:

1) 使用 immediate="true" 并且不显示验证错误

对于非验证按钮,设置 immediate="true" 并为 h:messages 设置:

<h:messages rendered="#{param['SHOW_VALIDATION']}" />

然后设置保存按钮(实际上应该尝试保存表单)以发送该参数:

<h:commandButton>
    <f:param name="SHOW_VALIDATION" value="true" />
</h:commandButton>

这会导致验证发生,但消息根本不显示,除非存在 SHOW_VALIDATION 参数。

2) 有条件地在 facelets 中声明验证:

<h:inputText>
    <f:validateRequired disabled="#{!param['VALIDATE']}" />
</h:inputText>

和保存按钮:

<h:commandButton>
    <f:param name="VALIDATE" value="true" />
</h:commandButton>

这会导致字段仅在存在 VALIDATE 参数时才有效(=按下保存按钮时)。

但这些似乎有点像黑客。我怎样才能简单地使用 JSR-303 Bean Validation 但在声明时跳过它?

【问题讨论】:

  • 嗯,除了 JSR-303 和带有 JSF 的 facelets 之外,我还没有尝试过任何其他验证解决方案。我认为这个问题与 JSF 相关,而不是 JSF-303 的错——就像他们忘记了“本机”支持在 JSF 中跳过验证。这似乎有很多混乱......

标签: jsf jsf-2 facelets bean-validation


【解决方案1】:

情况: 在页面代码中有一个带有很多输入字段的大表单。有几个按钮,每个按钮都指向单独 bean 中的单独操作。

对我有用的解决方案...

  1. 在页面代码中:将immediate="true" 添加到按钮并为要在bean 中使用的输入字段提供一个ID。
 <p:inputText id="someHtmlInputId"
               maxlength="30" 
               value="#{beanName.attr}" />


 <p:commandButton id="someHtmlButtonId"
                   actionListener="#{beanName.doStuff}" 
                   value="Button Label"
                   ajax="false"
                   immediate="true"/>
  1. 在 bean doStuff 方法中,通过名称片段获取参数,在输入名称 JSF 之前放置一些其他标识符,生成的参数名称可能如下所示:someFormId:j_idt54:someHtmlInputId
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
if (params != null) {
  for (String k: params.keySet())
      if (k.indexOf("someHtmlInputId") != -1)
          params.get(k); // This is Your input parameter value waiting to be processed ;)
}

【讨论】:

    【解决方案2】:

    我们刚刚发布了针对此问题的解决方案。 完整的细节在这里: http://www.springfuse.com/2011/12/15/skip-jsf-validation-depending-on-action.html

    简而言之,它利用了 validateBean 标记的绑定属性。 这样做可以让您拦截 validate 方法,并根据单击的按钮决定是否让它通过。

    【讨论】:

      【解决方案3】:

      您可以使用具有 disabled 属性的标签 f:validateBean 禁用 bean 验证。

      例子:

      <h:inputText value="#{bean.name}">
         <f:validateBean disabled="#{anotherBean.flag}"/>
      </h:inputText>
      

      【讨论】:

      • 这也是很好的信息。但是,为表单中的每个字段设置这个仍然有点麻烦......
      • 您也可以使用f:validateBean 包装多个输入组件,如下所示:stackoverflow.com/a/17503608/1725096
      【解决方案4】:

      将您的事件处理程序设置为immediate=true,并在退出之前调用FacesContext.renderResponse()

      更新

      示例表单中的修改:

      <h:form id="form">
          <h:commandButton value="Add row">
              <!-- Added immediate="true" to call bean.add() before validation phase -->
              <f:ajax execute="foo" listener="#{bean.add()}" render="foo" immediate="true"/>
          </h:commandButton>
          <h:dataTable id="foo" var="foo" value="#{bean.foos}">
              <h:column>
                  Name: <h:inputText id="field" value="#{foo.name}" required="true" />
                  <h:messages for="field" />
              </h:column>
              <h:column>
                  <h:commandButton value="Remove">
                      <!-- Added immediate="true" to call bean.remove() before validation phase -->
                      <f:ajax execute=":form:foo" listener="#{bean.remove(foo)}" render=":form:foo" immediate="true"/>
                  </h:commandButton>
              </h:column>
          </h:dataTable>
      </h:form>
      

      修改你的 bean 代码:

      ...
      public void add() {
          // Your add() code
          ...
          // Added FacesContext.renderResponse() call to skip to render response phase
          FacesContext.getCurrentInstance().renderResponse();
      }
      ...
      public void remove() {
          // Your remove() code
          ...
          // Added FacesContext.renderResponse() call to skip to render response phase
          FacesContext.getCurrentInstance().renderResponse();
      }
      ...
      

      【讨论】:

      • 在实践中你是如何做到的?能举个例子吗?
      • 明确一点:这样做会使 JSF 跳过第 3-5 阶段(过程验证、更新模型、调用应用程序)并直接进入第 6 阶段(呈现响应)。这没关系,因为结果有点像第 3 阶段的验证失败并且 JSF 会跳到第 6 阶段。如果有错误,请纠正我。下一步将是对此进行概括/组件化,因此不需要调用FacesContext.getCurrentInstance().renderResponse()。选择作为接受的答案,感谢您提供的好解决方案! :)
      • 我尝试了上述方法,总的来说效果很好。我的情况如下 - 我使用命令按钮向页面动态添加多个文本框。如果我使用immediate="false",页面验证将阻止更新,并且也不会显示。如果我使用immediate="true",并在执行 AJAX 请求之前修改了一些输入的值,则不会提交修改。任何想法如何实现在回调中读取更改的输入数据并且不同时触发验证?提前致谢
      • 我也有同样的问题。我希望更新模型,但跳过验证。我有一个带有不同标签的表单。我在选择不同的复选框时添加标签。 Ajax 渲染 p:tabView。但我不想丢失标签中已经输入或更改的信息。
      • 您可以在inputText 上设置immediate="true",它应该在调用任何操作之前更改值。
      猜你喜欢
      • 2015-01-10
      • 1970-01-01
      • 2011-10-01
      • 2013-11-23
      • 1970-01-01
      • 2012-03-06
      • 2011-07-24
      • 1970-01-01
      • 2011-01-31
      相关资源
      最近更新 更多