当 p/h:selectOneMenu 中为空 f:selectItem 时,有条件地跳过验证

Skip validation conditionally, when empty f:selectItem in p/h:selectOneMenu is selected

Parents :

<p:selectOneMenu id="parentList"
                 value="#{bean.selectedParent}"
                 converter="#{parentConverter}"
                 required="true">

        <f:selectItem itemLabel="Select" itemValue="#{null}"/>

        <f:selectItems var="parent"
                       value="#{bean.parentList}"
                       itemLabel="#{parent.parentName}"
                       itemValue="#{parent}"/>

        <p:ajax update="childrenList" listener="#{bean.setchildren}"/>
</p:selectOneMenu>

Children :

<p:selectOneMenu id="childrenList"
                     value="#{bean.selectedchild}"
                     converter="#{childConverter}"
                     required="true">

        <f:selectItem itemLabel="Select" itemValue="#{null}"/>

        <f:selectItems var="child"
                       value="#{bean.childrenList}"
                       itemLabel="#{child.childName}"
                       itemValue="#{child}"/>
</p:selectOneMenu>

托管 bean:

@Named
@ViewScoped
public class Bean implements Serializable {

    @Inject
    private Service service;

    private Parent selectedParent;
    private Child selectedChild;
    private List<Parent> parentList;
    private List<Child> childrenList;

    private static final long serialVersionUID = 1L;

    public Bean() {}

    @PostConstruct
    private void init() {
        parentList = service.getParentList();

        // Not necessary unless selectedParent is already available in edit mode.
        if(selectedParent != null) {
            childrenList = service.getChildrenListByParent(selectedParent);
        }
    }

    public void setChildren() {
        if(selectedParent != null) {
            childrenList = service.getChildrenListByParent(selectedParent);
        } else {
            childrenList = null;
        }
    }

    // Getters and setters.
}

children 列表将根据其 parent 进行填充,即 children 列表应仅包含与特定 parent 关联的 children。

When the first parent in the parent list is selected, the children list should be reset to empty i.e. children should not be visible without their parent.

由于parent列表有一个必填字段验证器,它会导致验证。 When the first item in the parent list is selected, the children list will be prevented from being updated because of required="true".技术上没有错,但是 children 的存在没有他们的 parent 可能会给 end-users 一个糟糕的体验。

应该发生的是,当 parent 列表中的第一项被选中时,它不应该导致验证,即有条件地跳过验证。

这样做的一种方法是检查 selectedChildchildrenList 本身是否为 null/empty。比如,

required="#{empty selectedChild or empty childrenList}"

但这似乎不是在这种情况下有条件地跳过验证的规范方法。

Does there exist a better way of skipping validation, when the first item in the parent list is selected so that the children list can be emptied as well along with the parent 列表(验证应该在所有其他情况下引起。例如,当表单本身被同步或异步提交时)?

基本上,您需要依赖于操作的验证。 IE。在调用特定 <p:ajax> 操作时跳过验证,而不是在其他操作上。

不幸的是,在视图中声明这确实不是一件容易的事。有几个tricks/workarounds。最常用的一种是仅检查是否(未)调用特定操作。

例如通过确定其客户端 ID 在 HTTP request parameter map as available by implicit EL object #{param}:

中的存在来检查是否调用了所需的保存按钮
<h:form>
    <p:selectOneMenu ... required="#{not empty param[save.clientId]}">
        ...
        <p:ajax ... />
    </p:selectOneMenu>
    <p:selectOneMenu ... required="true">
        ...
    </p:selectOneMenu>
    <p:commandButton binding="#{save}" ... />
</h:form>

或者检查组件自己的 <p:ajax> 是否未被调用,方法是确定组件自己的客户端 ID 是否不等于具有代表 ajax 源的预定义名称 javax.faces.source 的 HTTP 请求参数request(下面的#{component}是一个隐式EL变量,表示当前UIComponent):

<h:form>
    <p:selectOneMenu ... required="#{param['javax.faces.source'] ne component.clientId}">
        ...
        <p:ajax ... />
    </p:selectOneMenu>
    <p:selectOneMenu ... required="true">
        ...
    </p:selectOneMenu>
    <p:commandButton ... />
</h:form>

或者检查父表单是否由 UIForm#isSubmitted() 提交,这只会在 process="@form" 中使用“完整表单提交”时评估 true<p:ajax process> 默认为 @this,不会触发“完整表单提交”,而 <p:commandButton process> 默认为 @form,因此会触发“完整表单提交”):

<h:form binding="#{form}">
    <p:selectOneMenu ... required="#{form.submitted}">
        ...
        <p:ajax ... />
    </p:selectOneMenu>
    <p:selectOneMenu ... required="true">
        ...
    </p:selectOneMenu>
    <p:commandButton ... />
</h:form>

或者通过 UIComponent#getNamingContainer() 引用表单而不绑定表单(如果您知道组件树中的位置;如果表单是例如 2 命名容器父项,则使用 #{component.namingContainer.parent.namingContainer.submitted}) :

<h:form>
    <p:selectOneMenu ... required="#{component.namingContainer.submitted}">
        ...
        <p:ajax ... />
    </p:selectOneMenu>
    <p:selectOneMenu ... required="true">
        ...
    </p:selectOneMenu>
    <p:commandButton ... />
</h:form>

随你选。第一个解决方案之前已经提供过几次,因为它最容易被初学者理解和调整。

另请参阅:

  • How to let validation depend on the pressed button?
  • Primefaces dependent selectOneMenu and required="true"
  • How to disable the required tag on a selectOneMenu when p:ajax event=change?