valueChangeListener 只会在表单提交并且提交的值与初始值不同时调用。因此,当仅 HTML DOM change 事件被触发时,它不会被调用。如果您想在 HTML DOM change 事件期间提交表单,那么您需要在输入组件中添加另一个不带侦听器(!)的 <f:ajax/>。它将导致只处理当前组件的表单提交(如execute="@this")。
<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
<f:selectItems ... />
<f:ajax />
</h:selectOneMenu>
当使用 <f:ajax listener> 而不是 valueChangeListener 时,默认情况下它会在 HTML DOM change 事件期间执行。在 UICommand 组件和表示复选框或单选按钮的输入组件中,默认情况下它只会在 HTML DOM click 事件期间执行。
<h:selectOneMenu value="#{bean.value}">
<f:selectItems ... />
<f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>
另一个主要区别是valueChangeListener 方法是在PROCESS_VALIDATIONS 阶段结束时调用的。此时,模型中尚未更新提交的值。因此,您不能仅通过访问绑定到输入组件的value 的 bean 属性来获取它。您需要通过ValueChangeEvent#getNewValue() 获取。顺便说一下,ValueChangeEvent#getOldValue() 也可以使用旧值。
public void changeListener(ValueChangeEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
// ...
}
<f:ajax listener> 方法在INVOKE_APPLICATION 阶段被调用。那时,提交的值已经在模型中更新。您可以通过直接访问绑定到输入组件的value 的bean 属性来获取它。
private Object value; // +getter+setter.
public void ajaxListener(AjaxBehaviorEvent event) {
System.out.println(value); // Look, (new) value is already set.
}
另外,如果您需要根据提交的值更新 另一个 属性,那么当您使用 valueChangeListener 作为更新后的属性时它会失败可以在随后的UPDATE_MODEL_VALUES 阶段被提交的值覆盖。这正是您在旧的 JSF 1.x 应用程序/教程/资源中看到 valueChangeListener 与 immediate="true" 和 FacesContext#renderResponse() 结合使用以防止这种情况发生的原因。毕竟,使用valueChangeListener 执行业务操作实际上一直是一种hack/workaround。
总结:仅当您需要拦截实际值更改本身时才使用valueChangeListener。 IE。您实际上对 both 旧值和新值感兴趣(例如记录它们)。
public void changeListener(ValueChangeEvent event) {
changeLogger.log(event.getOldValue(), event.getNewValue());
}
仅当您需要对新更改的值执行业务操作时才使用<f:ajax listener>。 IE。您实际上只对新值感兴趣(例如,填充第二个下拉菜单)。
public void ajaxListener(AjaxBehaviorEvent event) {
selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}
如果您在执行业务操作时实际上也对旧值感兴趣,则回退到 valueChangeListener,但将其排队到 INVOKE_APPLICATION 阶段。
public void changeListener(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
System.out.println(newValue.equals(value)); // true
// ...
}