【问题标题】:commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated未调用 commandButton/commandLink/ajax 操作/侦听器方法或未设置/更新输入值
【发布时间】:2016-11-19 17:04:13
【问题描述】:

有时,当使用<h:commandLink><h:commandButton><f:ajax> 时,与标签关联的actionactionListenerlistener 方法根本不会被调用。或者,bean 属性未使用提交的 UIInput 值进行更新。

造成这种情况的可能原因和解决方法是什么?

【问题讨论】:

    标签: jsf jsf-2 action commandbutton commandlink


    【解决方案1】:

    简介

    每当UICommand 组件(<h:commandXxx><p:commandXxx> 等)未能调用关联的操作方法,或UIInput 组件(<h:inputXxx><p:inputXxxx> 等)未能处理提交的值和/或更新模型值,并且您在服务器日志中看不到任何可谷歌搜索的异常和/或警告,当您按照Exception handling in JSF ajax requests 配置 ajax 异常处理程序时,也没有在您设置以下上下文参数时web.xml,

    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    

    而且您在浏览器的 JavaScript 控制台中也看不到任何可谷歌搜索的错误和/或警告(在 Chrome/Firefox23+/IE9+ 中按 F12 打开 Web 开发人员工具集,然后打开 控制台 选项卡),然后解决下面的可能原因列表。

    可能的原因

    1. UICommandUIInput 组件必须放在 UIForm 组件内,例如&lt;h:form&gt;(因此不是纯 HTML &lt;form&gt;),否则无法将任何内容发送到服务器。 UICommand 组件也不能有 type="button" 属性,否则它将是一个死按钮,仅对 JavaScript onclick 有用。另见How to send form input values and invoke a method in JSF bean<h:commandButton> does not initiate a postback

    2. 您不能将多个UIForm 组件相互嵌套。这在 HTML 中是非法的。浏览器行为未指定。小心包含文件!您可以并行使用UIForm 组件,但它们在提交期间不会相互处理。您还应该注意“God Form”反模式;确保您不会无意中以相同的形式处理/验证所有其他(不可见的)输入(例如,在相同的形式中有一个带有所需输入的隐藏对话框)。另见How to use <h:form> in JSF page? Single form? Multiple forms? Nested forms?

    3. 不应该发生UIInput 值验证/转换错误。您可以使用&lt;h:messages&gt; 显示任何特定于输入的&lt;h:message&gt; 组件未显示的任何消息。不要忘记在&lt;f:ajax render&gt; 中包含&lt;h:messages&gt;id(如果有),以便在ajax 请求时也会更新它。另见h:messages does not display messages when p:commandButton is pressed

    4. 如果UICommandUIInput 组件被放置在像&lt;h:dataTable&gt;&lt;ui:repeat&gt; 等迭代组件中,那么您需要确保在迭代组件中保留完全相同的value表单提交请求的应用请求值阶段。 JSF 将重复它以找到单击的链接/按钮和提交的输入值。将 bean 放入视图范围和/或确保在 bean 的 @PostConstruct 中加载数据模型(因此不在 getter 方法中!)应该修复它。另见How and when should I load the model from database for h:dataTable

    5. 如果UICommandUIInput 组件包含在&lt;ui:include src="#{bean.include}"&gt; 等动态源中,那么您需要确保在表单提交请求的视图构建期间保留完全相同的#{bean.include} 值。 JSF 将在构建组件树期间重新执行它。将 bean 放入视图范围和/或确保将数据模型加载到 bean 的 @PostConstruct 中(因此不在 getter 方法中!)应该修复它。另见How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)

    6. 在表单提交请求的应用请求值阶段,组件及其所有父组件的 rendered 属性和任何父组件 &lt;c:if&gt;/&lt;c:when&gt;test 属性不应评估为 false . JSF 将重新检查它,作为防止篡改/黑客请求的一部分。将负责条件的变量存储在 @ViewScoped bean 中,或者确保您正确地预初始化 @PostConstruct 中的条件 @RequestScoped bean 应该可以修复它。这同样适用于组件的disabledreadonly 属性,在应用请求值阶段不应评估为true。另请参阅JSF CommandButton action not invokedForm submit in conditionally rendered component is not processedh:commandButton is not working once I wrap it in a <h:panelGroup rendered>Force JSF to process, validate and update readonly/disabled input components anyway

    7. UICommand 组件的onclick 属性和UIForm 组件的onsubmit 属性不应返回false 或导致JavaScript 错误。在&lt;h:commandLink&gt;&lt;f:ajax&gt; 的情况下,浏览器的 JS 控制台中也应该没有可见的 JS 错误。通常谷歌搜索确切的错误信息已经给你答案了。另见Manually adding / loading jQuery with PrimeFaces results in Uncaught TypeErrors

    8. 如果您通过 JSF 2.x &lt;f:ajax&gt; 或例如使用 Ajax PrimeFaces &lt;p:commandXxx&gt;,请确保您在主模板中有 &lt;h:head&gt; 而不是 &lt;head&gt;。否则 JSF 将无法自动包含包含 Ajax 函数的必要 JavaScript 文件。这会在浏览器的 JS 控制台中导致 JavaScript 错误,例如“mojarra 未定义”或“PrimeFaces 未定义”。另见h:commandLink actionlistener is not invoked when used with f:ajax and ui:repeat

    9. 如果您使用的是 Ajax,并且提交的值最终是 null,那么请确保 UIInputUICommand 感兴趣的组件被 &lt;f:ajax execute&gt; 或例如&lt;p:commandXxx process&gt;,否则它们将不会被执行/处理。另见Submitted form values not updated in model when adding <f:ajax> to <h:commandButton>Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes

    10. 如果提交的值仍然是null,并且您正在使用 CDI 来管理 bean,那么请确保从正确的包中导入范围注释,否则 CDI 将默认为 @Dependent,这会有效地重新创建对 EL 表达式的每一次求值的 bean。另见@SessionScoped bean looses scope and gets recreated all the time, fields become nullWhat is the default Managed Bean Scope in a JSF 2 application?

    11. 如果带有UICommand 按钮的&lt;h:form&gt; 的父级事先由来自同一页面中另一个表单的ajax 请求呈现/更新,那么在JSF 2.2 或更早版本中,第一个操作将始终失败。第二个和后续操作将起作用。这是由视图状态处理中的错误引起的,该错误报告为JSF spec issue 790,目前已在 JSF 2.3 中修复。对于较旧的 JSF 版本,您需要在 &lt;f:ajax&gt;render 中显式指定 &lt;h:form&gt; 的 ID。另见h:commandButton/h:commandLink does not work on first click, works only on second click

    12. 如果&lt;h:form&gt; 设置了enctype="multipart/form-data" 以支持文件上传,那么您需要确保您至少使用 JSF 2.2,或者负责解析 multipart/form- 的 servlet 过滤器数据请求配置正确,否则FacesServlet 最终将完全没有请求参数,因此无法应用请求值。如何配置这样的过滤器取决于正在使用的文件上传组件。对于 Tomahawk &lt;t:inputFileUpload&gt;,请查看 this answer,对于 PrimeFaces &lt;p:fileUpload&gt;,请查看 this answer。或者,如果您实际上根本没有上传文件,则完全删除该属性。

    13. 确保actionListenerActionEvent 参数是javax.faces.event.ActionEvent,因此不是java.awt.event.ActionEvent,这是大多数IDE 建议的第一个自动完成选项。如果您使用actionListener="#{bean.method}",那么没有参数也是错误的。如果您不想在方法中使用参数,请使用actionListener="#{bean.method()}"。或者,也许您实际上想使用action 而不是actionListener。另见Differences between action and actionListener

    14. 确保请求-响应链中没有PhaseListener 或任何EventListener 更改了JSF 生命周期以跳过调用操作阶段,例如调用FacesContext#renderResponse()FacesContext#responseComplete()

    15. 确保同一请求-响应链中没有FilterServlet 以某种方式阻止了FacesServlet 的请求。例如,登录/安全过滤器,例如 Spring Security。特别是在默认情况下根本没有 UI 反馈的 ajax 请求中。另见Spring Security 4 and PrimeFaces 5 AJAX request handling

    16. 如果您使用 PrimeFaces &lt;p:dialog&gt;&lt;p:overlayPanel&gt;,请确保它们有自己的 &lt;h:form&gt;。因为,这些组件在默认情况下被 JavaScript 重新定位到 HTML &lt;body&gt; 的末尾。因此,如果他们最初坐在&lt;form&gt; 中,那么他们现在将不再坐在&lt;form&gt; 中。另见p:commandbutton action doesn't work inside p:dialog

    17. 框架中的错误。例如,当使用具有defaultLabel 属性的rich:calendar UI 元素(或者,在某些情况下,rich:placeholder 子元素)时,RichFaces 具有“conversion error”。当没有为日历日期设置值时,此错误会阻止调用 bean 方法。跟踪框架错误可以通过从一个简单的工作示例开始并构建页面备份直到发现错误来完成。

    调试提示

    如果你仍然卡住,是时候调试了。在客户端,在 webbrowser 中按 F12 以打开 Web 开发人员工具集。单击 Console 选项卡,查看 JavaScript 控制台。它应该没有任何 JavaScript 错误。下面的屏幕截图是来自 Chrome 的示例,它演示了在未声明 &lt;h:head&gt; 的情况下提交启用 &lt;f:ajax&gt; 的按钮的情况(如上文第 7 点所述)。

    单击网络 选项卡以查看 HTTP 流量监视器。提交表单并调查请求标头和表单数据以及响应正文是否符合预期。下面的屏幕截图是来自 Chrome 的示例,它演示了一个简单的表单的成功 ajax 提交,其中包含一个 &lt;h:inputText&gt; 和一个 &lt;h:commandButton&gt;&lt;f:ajax execute="@form" render="@form"&gt;

    (警告:当您从生产环境中发布来自上述 HTTP 请求标头的屏幕截图时,请确保您对屏幕截图中的任何会话 cookie 进行加扰/混淆以避免会话劫持攻击!)

    在服务器端,确保服务器以调试模式启动。在您希望在处理表单提交期间调用的感兴趣的 JSF 组件的方法中放置一个调试断点。例如。如果是UICommand 组件,那将是UICommand#queueEvent(),如果是UIInput 组件,那将是UIInput#validate()。只需逐步执行代码并检查流程和变量是否符合预期。下面的截图是一个来自 Eclipse 调试器的例子。

    【讨论】:

    • 你的第二点让我思考了很久。我刚刚发现我的主文件中的 f:view 标记是我大部分问题的原因。可能是因为它呈现了一个表单,对吧?
    • @pauloguedes 我找不到任何说明 f:view 呈现表单的内容。我的理解是它只是一个容器。根据我的经验,f:view 不会渲染任何元素。
    • @balusc 关于第4点的一点澄清,如果commandLink不在dataTable本身中,还有关系吗?
    • 谢谢,重点是:将 bean 放入视图范围和/或确保将数据模型加载到 bean 的 (post) 构造函数中(因此不在 getter 方法中!)应该修复它。
    • @Kukeltje:那会引发 EL 异常(答案的第一段已经涵盖)
    【解决方案2】:

    如果您的h:commandLink 位于h:dataTable 内,则h:commandLink 可能不起作用的另一个原因:

    绑定到h:dataTable 的底层数据源也必须在单击链接时触发的第二个 JSF 生命周期中可用。

    所以如果底层数据源是请求范围的,h:commandLink 不起作用!

    【讨论】:

    • 好的,这对我来说并不完全清楚。无论如何,我希望我的回答是有帮助的,因为至少在我的情况下,我没有明确地处理 UICommand/UIData。 “解决方案”是将支持 bean 从请求范围提升到会话范围......
    • 我第二次 Jens 评论...将我的 RequestScoped bean 设置为 SessionScoped 对我的 dataTable 产生了影响 - 谢谢
    【解决方案3】:

    虽然我的答案不是 100% 适用,但大多数搜索引擎都认为这是第一次点击,但我还是决定发布它:

    如果您使用 PrimeFaces(或一些类似的 API)p:commandButtonp:commandLink,您可能忘记将 process="@this" 显式添加到您的命令组件中。

    正如 PrimeFaces 用户指南第 3.18 节所述,processupdate 的默认值都是 @form,这与您可能期望的普通 JSF f:ajax 或 RichFaces 的默认值完全相反,它们是 @分别为 987654330@ 和 render="@none"

    我花了很长时间才找到答案。 (...而且我认为使用与 JSF 不同的默认值是相当不明智的!)

    【讨论】:

    • PrimeFaces process 的默认值为 @form。因此,如果不是以这种方式调用该操作,而是在使用 @this 时调用该操作,那么我的回答中的第 3 点很可能适用。
    • 这不可能。我有一个p:commandButton,直到我添加process="@this" 才调用actionListener 方法。此外,PrimeFaces 用户指南明确列出了我在 3.18 和 3.19 节中提到的默认值。它在这里:primefaces.googlecode.com/files/primefaces_users_guide_3_4.pdf ...也许默认值已更改?
    • 这可能是文档中的错误。删除process="@this" 并添加&lt;p:messages autoUpdate="true"&gt;(或者只是读取排队但未显示的消息的服务器日志),您会看到实际上发生了转换/验证错误。
    • 你为什么删除这个问题stackoverflow.com/questions/60673695/…
    • 我认为它现在可能没有太大价值...我取消删除它。
    【解决方案4】:

    我还要提一件事,涉及 Primefaces 的 p:commandButton

    当您将p:commandButton 用于需要在服务器上执行的操作时,您不能使用type="button",因为这是用于执行自定义javascript 的按钮导致向服务器发出 ajax/非 ajax 请求。

    为此,您可以分配type 属性(默认值为"submit"),也可以显式使用type="submit"

    希望这会对某人有所帮助!

    【讨论】:

    • 这是我在我们的一个页面中的主要问题,接受的答案中没有一个点让我们更接近,您在哪里找到这些信息?
    • 好吧,我遇到过很多次这个问题,我研究并发现p:commandButton具有type属性的几个值,而button是一个与客户端有关的所有值.在Primefaces doc 中找到它有点困难,但这里有一个链接:developer.am/primefaces/…
    • 您的提交提示解决了我几天来一直面临的问题。非常感谢你的帖子!
    • 谢谢,我很高兴。我故意提出这个答案,因为我们很多人都有这样的问题。我也迷失了几天,直到意识到发生了什么。
    【解决方案5】:

    我自己也遇到了这个问题,并找到了导致此问题的另一个原因。 如果您的后备 bean 中没有针对 *.xhtml 中使用的属性的 setter 方法,则根本不会调用该操作。

    【讨论】:

    • 它应该会产生一个相当不言自明的PropertyNotWritableException。如果您没有看到它,可能是您在没有适当的 ajax 异常处理程序的情况下触发了 ajax 请求,但您应该在服务器日志中看到它。
    • 直到我做了 p:commandButton 的 ajax="false" 才显示异常。
    • 感谢上帝你救了我的命
    【解决方案6】:

    我最近遇到了一个问题,即 UICommand 没有在使用 IBM Extended Faces Components 的 JSF 1.2 应用程序中调用。

    我在数据表的一行上有一个命令按钮(扩展版本,所以&lt;hx:datatable&gt;)并且 UICommand 不会从表中的某些行触发(不会触发的行是大于默认值的行行显示大小)。

    我有一个用于选择要显示的行数的下拉组件。支持此字段的值在 RequestScope 中。支持表本身的数据在某种ViewScope 中(实际上,暂时在SessionScope 中)。

    如果通过控件增加行显示,哪个值也绑定到数据表的rows 属性,则由于此更改而显示的所有行都不会在单击时触发 UICommand。

    将此属性放在与表数据本身相同的范围内解决了问题。

    我认为这在上面的 BalusC #4 中有所提及,但不仅表值需要是 View 或 Session 范围的,而且控制要在该表上显示的行数的属性也是如此。

    【讨论】:

      【解决方案7】:

      我也遇到了这个问题,只是在打开浏览器的 Web 控制台后才真正开始研究根本原因。在那之前,我无法收到任何错误消息(即使使用&lt;p:messages&gt;)。 Web 控制台显示从 &lt;h:commandButton type="submit" action="#{myBean.submit}"&gt; 返回的 HTTP 405 状态代码。

      在我的例子中,我混合了 vanilla HttpServlet 通过 Auth0 和 JSF facelets 提供 OAuth 身份验证,以及执行我的应用程序视图和业务逻辑的 bean。

      一旦我重构了我的 web.xml,并删除了一个中间人-servlet,它就“神奇地”工作了。

      归根结底,问题在于中间人-servlet 使用 RequestDispatcher.forward(...) 从 HttpServlet 环境重定向到 JSF 环境,而在它之前调用的 servlet 使用 HttpServletResponse.sendRedirect 重定向(...)。

      基本上,使用 sendRedirect() 允许 JSF“容器”控制,而 RequestDispatcher.forward() 显然不是。

      我不知道为什么 facelet 能够访问 bean 属性但无法设置它们,这显然是为了取消 servlet 和 JSF 的混合,但我希望这可以帮助某人避免很多时间头撞桌子。

      【讨论】:

        【解决方案8】:

        我在调试&lt;h:commandLink&gt;richfaces datatable 中的操作拒绝触发的问题时获得了很多乐趣。该表曾经在某个时候工作,但无缘无故停止了。我千方百计,却发现我的rich:datatable 使用了错误的rowKeyConverter,它返回的空值被richfaces 愉快地用作行键。这阻止了我的 &lt;h:commandLink&gt; 操作被调用。

        【讨论】:

          【解决方案9】:

          另一种可能性:如果症状是第一次调用有效,但后续调用无效,则您可能正在使用带有 JSF 2.2 的 PrimeFaces 3.x,详见此处:No ViewState is sent

          【讨论】:

            【解决方案10】:

            我解决了放置的问题:

            <h:commandButton class="btn btn-danger" value = "Remove" action="#{deleteEmployeeBean.delete}"></h:commandButton>
            

            在:

            <h:form>
                 <h:commandButton class="btn btn-danger" value = "Remove" action="#{deleteEmployeeBean.delete}"></h:commandButton>
            </h:form>
            

            【讨论】:

            • 这是 >600 人赞成的答案中的第一名。无需将其写为单独的答案。
            【解决方案11】:

            这是对我有用的解决方案。

            <p:commandButton id="b1" value="Save" process="userGroupSetupForm"
                                actionListener="#{userGroupSetupController.saveData()}" 
                                update="growl userGroupList userGroupSetupForm" />
            

            这里,process="userGroupSetupForm" 属性对于 Ajax 调用是必需的。 actionListener 正在从 @ViewScope Bean 调用一个方法。同时更新咆哮消息,Datatable: userGroupList 和 Form: userGroupSetupForm。

            【讨论】:

            • 这解决了什么问题?你改变了什么来解决它?为什么您的更改解决了您的问题?您确定在最佳答案中没有提到它吗?
            【解决方案12】:
            <ui:composition>  
              <h:form id="form1">
                <p:dialog id="dialog1">
                  <p:commandButton value="Save" action="#{bean.method1}" /> <!--Working-->
                </p:dialog>
              </h:form>
            
              <h:form id="form2">
                <p:dialog id="dialog2">
                  <p:commandButton value="Save" action="#{bean.method2}" /> <!--Not Working-->
                </p:dialog>
              </h:form>
            </ui:composition>
            

            解决;

            <ui:composition>  
              <h:form id="form1">
                <p:dialog id="dialog1">
                  <p:commandButton value="Save" action="#{bean.method1}" />   <!-- Working  -->
                </p:dialog>
            
                <p:dialog id="dialog2">
                  <p:commandButton value="Save" action="#{bean.method2}" />   <!--Working  -->
                </p:dialog>
              </h:form>
              <h:form id="form2">
                <!-- ..........  -->
              </h:form>
            </ui:composition>
            

            【讨论】:

            • 对不起,但这在我看来完全不正确。您有效地声明 2 个对话框需要以自己的形式工作。你有 99% 的把握有一个不同的问题,你解决了,你现在认为这是解决方案......
            • 这是包含页面。也许这可能会导致问题。您应该在投票前对其进行测试。
            • 不,你应该在发布之前创建一个minimal reproducible example(只有这样我才能测试)......是的,包括可能导致对话框和表单出现问题,但问题仍然不是你似乎想在这里解决什么。您的第一个示例非常好,第二个示例 100% 确定不是不存在问题的解决方案
            • 无论如何。我的应用程序运行良好。我认为对话框在表单中的位置并不重要。我必须创建第二个表单来解决另一个问题。
            • 您的应用程序可能正在运行,但从原始问题和您的“解决方案”来看,这并不清楚/可见。此外,您说_“我认为对话框在表单中的位置并不重要。”_但在您的回答中,它们在哪里似乎很重要。一个支持我的说法的矛盾。对不起,但你的答案显然是错误的......(已经有另一个反对意见)
            猜你喜欢
            相关资源
            最近更新 更多