父母:
<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>
孩子:
<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.
}
子列表将根据它们的父列表填充,即子列表应该只包含与特定父列表关联的子列表。
当父列表中的第一个父列表被选中时,子列表应该被重置为空,即如果没有父列表,子列表不应该可见。
由于父列表有一个必需的字段验证器,它导致验证。当父列表中的第一项被选中时,子列表将因为required="true"
而被阻止更新。这在技术上没有什么问题,但是没有父母的孩子可能会给最终用户带来不好的体验。
应该发生的是,当父列表中的第一项被选中时,它不应该导致验证,即验证将有条件地跳过。
这样做的一种方法是检查selectedChild
或childrenList
本身是null/empty。例如,
required="#{empty selectedChild or empty childrenList}"
但是在这种情况下,这似乎不是有条件跳过验证的规范方式。
是否存在更好的跳过验证的方法,当父列表中的第一个项目被选中时,子列表可以与父列表一起被清空(验证应该在所有其他情况下引起)。例如,当表单本身以同步或异步方式提交时?
基本上,您需要依赖于操作的验证。例如,当调用特定的<p:ajax>
操作时跳过验证,而不是在其他操作上跳过验证。
确实不容易。有几个技巧/变通方法。最常用的一种是检查特定的操作是否被调用。
。检查是否调用了所需的保存按钮,方法是通过隐式EL对象#{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是否不等于HTTP请求参数的预定义名称javax.faces.source
,表示ajax请求的来源(下面的#{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()
提交,它只会在"完整表单提交"时评估true
;与process="@form"
中的使用相同(<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>
随你挑。第一个解决方案之前已经提供了几次,因为它最容易被初学者理解和调整。
参见:
- 如何让验证依赖于按下的按钮?
- Primefaces dependent selectonmenu and required="true"
- 如何禁用所需的标签在一个selectOneMenu当p:ajax事件=变化?