PrimeFaces的 p:commandButton在 p:confirmDialog中将ajax设置为false时不会触发action
我有一个基本需求,使用PrimeFaces的 p:confirmDialog来提问一个简单的是/否问题。最初的想法是使用p:commandButton ajax="true",调用一个动作来更新状态并停留在同一页(使用PF的 @form UI更新+ 向用户显示消息):
<p:confirmDialog global="true"
showEffect="fade"
hideEffect="fade"
responsive="true"
style="max-width: 1000px;">
<p:commandButton value="No"
styleClass="ui-button-secondary ui-confirmdialog-no"
type="button" />
<p:commandButton value="Yes"
ajax="true"
action="#{ergebnisTextbausteinStatusBean.finalizeStatus()}"
process="@this"
update="@form template_msg_summary"
styleClass="ui-confirmdialog-yes" />
</p:confirmDialog>
这按预期工作。动作被调用,数据库中的状态被更新,UI显示一切正常。
现在客户决定改变这个流程,在Yes时导航到另一页(如果成功的话)。
因此,选择尝试一个 <p:commandButton ajax="false">(因为在整个项目中都在使用,所以这个按钮的代码基本上是从项目中其他地方调整而来)。
对话框+ 使用 ajax="false" 的按钮:
<p:confirmDialog global="true"
showEffect="fade"
hideEffect="fade"
responsive="true"
style="max-width: 1000px;">
<p:commandButton value="No"
styleClass="ui-button-secondary ui-confirmdialog-no"
type="button" />
<template:formElementSpacer elementWidth="6" elementWidthM="2" />
<p:commandButton value="Yes"
ajax="false"
action="#{ergebnisTextbausteinStatusBean.finalizeStatus()}"
styleClass="ui-confirmdialog-yes" />
</p:confirmDialog>
然而,动作从未被调用。
随后我安装了一个JSF PhaseListener(在faces-config.xml中挂接)来检查一些事情:
public class AnyPhaseListener implements PhaseListener {
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
@Override
public void beforePhase(PhaseEvent event) {
System.out.println("----------------- " + getClass().getSimpleName() + " BEFORE PHASE: " + event.getPhaseId());
}
@Override
public void afterPhase(PhaseEvent event) {
System.out.println("----------------- " + getClass().getSimpleName() + " AFTER PHASE: " + event.getPhaseId());
}
}
它会打印出每个请求访问的所有阶段。
对于上面这个(工作中的)AJAX true按钮,记录的系统输出如下:
[3/26/26 19:58:26:095 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RESTORE_VIEW(1)
[3/26/26 19:58:26:105 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RESTORE_VIEW(1)
[3/26/26 19:58:26:105 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:58:26:106 CET] 000016f1 HtmlRendererU W There should always be a submitted value for an input if it is rendered, its form is submitted, and it was not originally rendered disabled or read-only. You cannot submit a form after disabling an input element via javascript. Consider setting read-only to true instead or resetting the disabled value back to false prior to form submission. Component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /pages/auskunftsersuchen/ergebnisTextbaustein.xhtml][Class: javax.faces.component.html.HtmlBody,Id: j_id_d][Class: javax.faces.component.html.HtmlInputHidden,Id: j_id_3h]}
[3/26/26 19:58:26:106 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:58:26:106 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:58:26:107 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:58:26:107 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:58:26:108 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:58:26:108 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:58:26:108 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:58:26:109 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RENDER_RESPONSE(6)
[3/26/26 19:58:26:123 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RENDER_RESPONSE(6)
[3/26/26 19:58:28:160 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RESTORE_VIEW(1)
[3/26/26 19:58:28:172 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RESTORE_VIEW(1)
[3/26/26 19:58:28:173 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:58:28:173 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:58:28:173 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:58:28:174 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:58:28:174 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:58:28:174 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:58:28:174 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:58:28:175 CET] 000016f1 SystemOut O ------------------------- ErgebnisTextbausteinStatusBean.finalizeStatus()!
[3/26/26 19:58:28:175 CET] 000016f1 SystemOut O ------------------------- 2
[3/26/26 19:58:28:649 CET] 000016f1 SystemOut O ------------------------- 3
[3/26/26 19:58:30:510 CET] 000016f1 SystemOut O ------------------------- 4
[3/26/26 19:58:30:510 CET] 000016f1 SystemOut O ------------------------- 5
[3/26/26 19:58:30:510 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:58:30:511 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RENDER_RESPONSE(6)
[3/26/26 19:58:30:661 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RENDER_RESPONSE(6)
[3/26/26 19:58:34:658 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RESTORE_VIEW(1)
[3/26/26 19:58:34:667 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RESTORE_VIEW(1)
[3/26/26 19:58:34:667 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:58:34:668 CET] 000016f1 HtmlRendererU W There should always be a submitted value for an input if it is rendered, its form is submitted, and it was not originally rendered disabled or read-only. You cannot submit a form after disabling an input element via javascript. Consider setting read-only to true instead or resetting the disabled value back to false prior to form submission. Component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /pages/auskunftsersuchen/ergebnisTextbaustein.xhtml][Class: javax.faces.component.html.HtmlBody,Id: j_id_d][Class: javax.faces.component.html.HtmlInputHidden,Id: j_id_3h]}
[3/26/26 19:58:34:668 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:58:34:668 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:58:34:669 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:58:34:669 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:58:34:669 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:58:34:670 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:58:34:670 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:58:34:670 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RENDER_RESPONSE(6)
[3/26/26 19:58:34:680 CET] 000016f1 SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RENDER_RESPONSE(6)
请注意对 finalizeStatus() 的调用,以及我在动作方法中放入的2-3-4-5条系统输出。
AJAX false按钮的输出则不同:
[3/26/26 19:36:38:871 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RESTORE_VIEW(1)
[3/26/26 19:36:38:879 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RESTORE_VIEW(1)
[3/26/26 19:36:38:880 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:36:38:880 CET] 000016eb HtmlRendererU W There should always be a submitted value for an input if it is rendered, its form is submitted, and it was not originally rendered disabled or read-only. You cannot submit a form after disabling an input element via javascript. Consider setting read-only to true instead or resetting the disabled value back to false prior to form submission. Component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /pages/auskunftsersuchen/ergebnisTextbaustein.xhtml][Class: javax.faces.component.html.HtmlBody,Id: j_id_d][Class: javax.faces.component.html.HtmlInputHidden,Id: j_id_3h]}
[3/26/26 19:36:38:880 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:36:38:880 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:36:38:881 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:36:38:881 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:36:38:881 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:36:38:882 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:36:38:882 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:36:38:882 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RENDER_RESPONSE(6)
[3/26/26 19:36:38:891 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RENDER_RESPONSE(6)
[3/26/26 19:36:45:492 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RESTORE_VIEW(1)
[3/26/26 19:36:45:500 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RESTORE_VIEW(1)
[3/26/26 19:36:45:501 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:36:45:501 CET] 000016eb HtmlRendererU W There should always be a submitted value for an input if it is rendered, its form is submitted, and it was not originally rendered disabled or read-only. You cannot submit a form after disabling an input element via javascript. Consider setting read-only to true instead or resetting the disabled value back to false prior to form submission. Component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /pages/auskunftsersuchen/ergebnisTextbaustein.xhtml][Class: javax.faces.component.html.HtmlBody,Id: j_id_d][Class: javax.faces.component.html.HtmlInputHidden,Id: j_id_3h]}
[3/26/26 19:36:45:501 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: APPLY_REQUEST_VALUES(2)
[3/26/26 19:36:45:501 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:36:45:502 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: PROCESS_VALIDATIONS(3)
[3/26/26 19:36:45:502 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:36:45:503 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: UPDATE_MODEL_VALUES(4)
[3/26/26 19:36:45:503 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:36:45:503 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: INVOKE_APPLICATION(5)
[3/26/26 19:36:45:504 CET] 000016eb SystemOut O ----------------- AnyPhaseListener BEFORE PHASE: RENDER_RESPONSE(6)
[3/26/26 19:36:45:515 CET] 000016eb SystemOut O ----------------- AnyPhaseListener AFTER PHASE: RENDER_RESPONSE(6)
这完全没有系统输出,这个输出让我看到中间缺少一个完整的请求。
不知道另外两个请求,一个在前一个在后,但我们假设它们没问题。
这里的代码/解决方案有什么问题?
我们是在一台较旧的WebSphere 9服务器上,使用JSF 2.2和 PrimeFaces 13.0.10
解决方案
这里有两个注意点。
首先,需要用 ajax=false 和 action="#{...}" 声明显示对话框的按钮,并使用一个嵌套的 p:confirm(见下方PF bug的代码示例):
然后(第二点)然而,PrimeFaces 13会触发以下bug:
https://github.com/primefaces/primefaces/issues/12718
为了解决这个问题,根本不要使用 p:confirm:
<p:commandButton value="Do this"
type="button"
onclick="PF('myDialog').show();" />
在 p:confirmDialog 上使用一个经典的 widgetVar="",删除 global="true",手动调用 PF('myDialog').show() 和 PF('myDialog').hide(),并让对话框中的按钮调用动作:
<p:confirmDialog widgetVar="myDialog"
header="Security Check"
message="Lala"
responsive="true" >
<p:commandButton value="No"
type="button"
onclick="PF('myDialog').hide();" />
<p:commandButton value="Yes"
ajax="false"
action="#{statusBean.doSomething()}" />
</p:confirmDialog>