为什么 JSF 表单保持验证样式,即使它上面有值?
Why JSF form maintain the validation styles even if has values on it?
我有一个表单 (frmAddPax) 可以添加一些用户数据。可以手动或通过条形码 reader 提交此数据。当按下按钮 "Escanear" 时,此按钮会调用另一个表单 (frmScan) 的对话框。
此表单从条形码 reader 中读取一些数据,并在托管 bean 中处理此数据。数据创建一个在原始表单 (frmAddPax) 中使用的对象。
问题是所有表单都有样式,因为上面没有任何数据,所有必填字段都有 required="true" 属性。
如果我再次按下 "Escanear" 按钮并扫描相同的数据,它会正常显示表格。
我认为这可能是因为在数据准备好以表单形式更新之前,验证过程发生了,但正如我在一些问题中看到的那样 action 和 actionListener 事件发生在 update 过程之前,所以我不知道。
这是表格的代码:
<h:form id="frmAddPax"
rendered="#{MB.renderStatus.isRenderFormAddPax()}">
<p:panelGrid styleClass="no-border">
<p:row>
<p:column>
<h:outputText
value="#{label['manageVipLoungeEntrance.addPassenger.firstName']} />
</p:column>
<p:column>
<p:inputText required="true"
value="#{manageVipLoungeEntranceExtMB.passenger.firstName}"
style="text-transform: uppercase;" converter="upperCaseConverter">
<f:ajax event="blur" update="@this" render="@this" />
</p:inputText>
</p:column>
...
...
<!-- BOTON ESCANEAR AGREGAR PASAJERO -->
<p:column>
<p:commandButton inmediate="true"
value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" update=":frmScan"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
</p:column>
<!-- BOTON ESCANEAR AGREGAR PASAJERO -->
</p:row>
</p:panelGrid>
这是调用 "Escanear" 按钮的代码:
<p:commandButton inmediate="true"
value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" update=":frmScan"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
这是处理条形码读取并使用处理后的数据更新原始表单的小部件的代码。
<p:dialog widgetVar="wgvScan" modal="true" showEffect="fade"
closeOnEscape="true" resizable="false">
<h:form id="frmScan">
<p:graphicImage value="../resources/images/barCode.png"
rendered="#{manageVipLoungeEntranceExtMB.showTablePassenger!=true}" />
<p:inputText id="itbarcode"
rendered="#{manageVipLoungeEntranceExtMB.showTablePassenger!=true}"
value="#{manageVipLoungeEntranceExtMB.barCode}" onfocus="true"
autocomplete="off" styleClass="insertData"
style="background:#ffffff; position:absolute;left:-7000;" />
<p:commandButton id="cmdReadBarcode" style="display:none"
onclick="showLocalDate()"
actionListener="#{manageVipLoungeEntranceExtMB.readBarCode}"
update=":frmAddPax :growl">
</p:commandButton>
<p:defaultCommand target="cmdReadBarcode" />
...
</h:form>
[编辑]
@alibttb 回答让我找到解决方案。
我在调用对话框以侦听扫描仪的按钮前添加了一个 remoteCommand。
<p:remoteCommand name="refreshForm" process=":frmAddPax" update=":frmAddPax" />
<p:commandButton
value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" process="@this" update=":frmScan"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
</p:column>
在处理条形码的表单对话框中,我添加了一个 onHide 属性来调用 remoteCommand。
我按照建议将对话框表单的附件更改为表单对话框。
<h:form id="frmScan">
<p:dialog widgetVar="wgvScan" modal="true" showEffect="fade"
closeOnEscape="true" resizable="false" onHide="refreshForm()">
当你点击 Escanear 按钮时会发生什么,你正在处理整个表单,因此提交所有具有空值的字段,这将导致验证错误,你的按钮是立即发生以下情况:
- actionListener 是即时的,因此它首先被调用,托管 bean 中填充来自条形码扫描器的数据。
- 正在验证表单数据,但它无效,因此在所有输入上都设置了 inValid 标志。
- 在服务器上创建包含表单更新的响应,显示来自托管 bean 的新值和来自验证过程的输入的 inValid 状态。
请注意,提交的数据(空值)未应用于模型,因为它无效。
要解决此问题,只需在您的按钮上使用部分处理功能,并删除立即数="true",这只是一个糟糕的设计。
只需将 Escanear 按钮中的 immediate="true" 替换为 process="@this"。
如果您不熟悉 JSF 和 primefaces 的部分处理功能,您应该看看它。
如果您确实需要在扫描完成后提交表单进行验证,那么您需要使用 p:remoteCommand
在 actionListener 完成后提交表单:
<p:remoteCommand name="validateForm" process="@form"/>
<p:commandButton value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" update=":frmScan" process="@this"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
并以另一种形式frmScan
做:
<h:form id="frmScan">
<p:dialog widgetVar="wgvScan" modal="true" showEffect="fade"
closeOnEscape="true" resizable="false" onHide="validateForm()">
....
....complete your code
p:remoteCommand
的名称变成了一个 javascript 函数,可以在隐藏扫描对话框后回调。
注意 在浏览器中打开开发控制台并观察两个请求,一个是更新表单和关闭对话框,另一个是 p:remoteCommand
引起的验证表格。
注1(与你的问题无关)我用frmScan
把p:dialog
括起来,这是正确的做法,表单应该围绕对话框而不是相反。
我有一个表单 (frmAddPax) 可以添加一些用户数据。可以手动或通过条形码 reader 提交此数据。当按下按钮 "Escanear" 时,此按钮会调用另一个表单 (frmScan) 的对话框。
此表单从条形码 reader 中读取一些数据,并在托管 bean 中处理此数据。数据创建一个在原始表单 (frmAddPax) 中使用的对象。
问题是所有表单都有样式,因为上面没有任何数据,所有必填字段都有 required="true" 属性。
如果我再次按下 "Escanear" 按钮并扫描相同的数据,它会正常显示表格。
我认为这可能是因为在数据准备好以表单形式更新之前,验证过程发生了,但正如我在一些问题中看到的那样 action 和 actionListener 事件发生在 update 过程之前,所以我不知道。
这是表格的代码:
<h:form id="frmAddPax"
rendered="#{MB.renderStatus.isRenderFormAddPax()}">
<p:panelGrid styleClass="no-border">
<p:row>
<p:column>
<h:outputText
value="#{label['manageVipLoungeEntrance.addPassenger.firstName']} />
</p:column>
<p:column>
<p:inputText required="true"
value="#{manageVipLoungeEntranceExtMB.passenger.firstName}"
style="text-transform: uppercase;" converter="upperCaseConverter">
<f:ajax event="blur" update="@this" render="@this" />
</p:inputText>
</p:column>
...
...
<!-- BOTON ESCANEAR AGREGAR PASAJERO -->
<p:column>
<p:commandButton inmediate="true"
value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" update=":frmScan"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
</p:column>
<!-- BOTON ESCANEAR AGREGAR PASAJERO -->
</p:row>
</p:panelGrid>
这是调用 "Escanear" 按钮的代码:
<p:commandButton inmediate="true"
value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" update=":frmScan"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
这是处理条形码读取并使用处理后的数据更新原始表单的小部件的代码。
<p:dialog widgetVar="wgvScan" modal="true" showEffect="fade"
closeOnEscape="true" resizable="false">
<h:form id="frmScan">
<p:graphicImage value="../resources/images/barCode.png"
rendered="#{manageVipLoungeEntranceExtMB.showTablePassenger!=true}" />
<p:inputText id="itbarcode"
rendered="#{manageVipLoungeEntranceExtMB.showTablePassenger!=true}"
value="#{manageVipLoungeEntranceExtMB.barCode}" onfocus="true"
autocomplete="off" styleClass="insertData"
style="background:#ffffff; position:absolute;left:-7000;" />
<p:commandButton id="cmdReadBarcode" style="display:none"
onclick="showLocalDate()"
actionListener="#{manageVipLoungeEntranceExtMB.readBarCode}"
update=":frmAddPax :growl">
</p:commandButton>
<p:defaultCommand target="cmdReadBarcode" />
...
</h:form>
[编辑] @alibttb 回答让我找到解决方案。
我在调用对话框以侦听扫描仪的按钮前添加了一个 remoteCommand。
<p:remoteCommand name="refreshForm" process=":frmAddPax" update=":frmAddPax" />
<p:commandButton
value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" process="@this" update=":frmScan"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
</p:column>
在处理条形码的表单对话框中,我添加了一个 onHide 属性来调用 remoteCommand。 我按照建议将对话框表单的附件更改为表单对话框。
<h:form id="frmScan">
<p:dialog widgetVar="wgvScan" modal="true" showEffect="fade"
closeOnEscape="true" resizable="false" onHide="refreshForm()">
当你点击 Escanear 按钮时会发生什么,你正在处理整个表单,因此提交所有具有空值的字段,这将导致验证错误,你的按钮是立即发生以下情况:
- actionListener 是即时的,因此它首先被调用,托管 bean 中填充来自条形码扫描器的数据。
- 正在验证表单数据,但它无效,因此在所有输入上都设置了 inValid 标志。
- 在服务器上创建包含表单更新的响应,显示来自托管 bean 的新值和来自验证过程的输入的 inValid 状态。
请注意,提交的数据(空值)未应用于模型,因为它无效。
要解决此问题,只需在您的按钮上使用部分处理功能,并删除立即数="true",这只是一个糟糕的设计。
只需将 Escanear 按钮中的 immediate="true" 替换为 process="@this"。
如果您不熟悉 JSF 和 primefaces 的部分处理功能,您应该看看它。
如果您确实需要在扫描完成后提交表单进行验证,那么您需要使用 p:remoteCommand
在 actionListener 完成后提交表单:
<p:remoteCommand name="validateForm" process="@form"/>
<p:commandButton value="#{label['manageVipLoungeEntrance.addPassenger.button.scan']}"
onclick="showLocalDate()" update=":frmScan" process="@this"
actionListener="#{manageVipLoungeEntranceExtMB.clear}"
oncomplete="{wgvScan.show()}" />
并以另一种形式frmScan
做:
<h:form id="frmScan">
<p:dialog widgetVar="wgvScan" modal="true" showEffect="fade"
closeOnEscape="true" resizable="false" onHide="validateForm()">
....
....complete your code
p:remoteCommand
的名称变成了一个 javascript 函数,可以在隐藏扫描对话框后回调。
注意 在浏览器中打开开发控制台并观察两个请求,一个是更新表单和关闭对话框,另一个是 p:remoteCommand
引起的验证表格。
注1(与你的问题无关)我用frmScan
把p:dialog
括起来,这是正确的做法,表单应该围绕对话框而不是相反。