如何从 Javascript 触发 JSF 中的 AJAX 调用并避免使用全局函数作为回调处理程序?

How to Trigger an AJAX call in JSF from Javascript and avoid using global functions as callback handlers?

我想将使用 f:ajax、f:event 和 h:commandLink(及其 action 属性)编写的 JSF AJAX 触发代码移动到 Javascript通过使用 jsf.util.ajax。 - 为什么 ?因为要避免全局编写处理函数来处理回调。我们正在将 JS 代码迁移到 AMD Require JS,而 JSF 运行得不是很好

以前,从 JSF 中的视图 XHTML 文件触发 AJAX 调用的代码是这样写的:

<h:commandLink id="elemID" tabindex="-1" rendered="#{something.isEnabled ('showSomeLink')}">
    <f:ajax render="#{someBean.getElemId('LinkID')}" onevent="renderAjax" />
    <jsft:event type="ajax"> someBean.someMethod(); </jsft:event>
</h:commandLink>

以上标记在 HTML 中呈现如下:

<a href="#" onclick="jsf.util.chain(document.getElementById('elemID'), event,'jsf.ajax.request(\'elemID\',event,{render:\'LinkID\',onevent:renderAjax,\'javax.faces.behavior.event\':\'action\'})'); return false;" id="elemID" name="elemID" tabindex="-1"></a>

然后,在此 link 上触发了点击事件以执行 AJAX 和操作。


使用了 jsft:event,其行为类似于其对应的 f:event

我不想用这个,因为它强制在全局定义 JS 函数。在这种情况下,f:ajax

的 onevent 属性中的 renderAjax()

如您所见,这依赖于一个名为 renderAjax() 的全局定义函数作为回调函数来执行某些操作并在 UI 端呈现。

问题是我们正在转向 AMD 并且需要 JS,而我们没有在我们的代码中定义任何全局变量的范围(我们可以但我们不想)。即使我们这样做了,这个 ajax 调用也会在 Require AMD 加载

之前触发

所以我尝试在 AMD 模块内用 JS 从客户端重写这个 ajax 调用,就像这样(来自上面提到的生成的 HTML 片段的 onclick)

(注意,我不知道如何在这段JS代码中提到action属性。)

jsf.util.chain(document.getElementById('elemID'), event,'jsf.ajax.request(\'elemID\',event,{render:\'LinkID\',onevent:renderAjax,\'javax.faces.behavior.event\':\'action\'})');

一旦我用 JS 编写了上述代码,我就删除了 f:ajax(因为 renderonevent 属性包含在 JS 代码本身中)和 jsft:event 从视图中,在 XHTML 文件中。一旦我删除它,它就会开始导致页面不断重新加载,成为一个无限循环。我不得不停止服务器并将代码恢复到原来的状态。

接下来,我认为 h:commandLink 是问题所在,因此决定通过 h:outputLinkh:commandLink 替换为正常呈现的锚标记。

不幸的是,它没有动作属性。关键是,虽然元素被渲染了,

jsf.util.chain(document.getElementById('elemID'), event,'jsf.ajax.request(\'elemID\',event,{render:\'LinkID\',onevent:renderAjax,\'javax.faces.behavior.event\':\'action\'})');

由于我的带有 h:outputLink 的代码没有 action 属性,它不会执行托管 Bean 中的方法。

假设我包含了 h:commandLink,没有 jsft:eventf:ajax,那么它会导致无限的页面重新加载。

我什至在删除 jsft:eventf:ajax 后将 action 属性添加到 h:commandLink 。我让它与下面的更新一起工作。

<h:commandLink id="elemID" tabindex="-1" rendered="#{something.isEnabled ('showSomeLink')}" action="#{someBean.someMethod()}">
            <f:ajax />  
        </h:commandLink>

然后在 JS 中使用以下内容:

jsf.util.chain(document.getElementById('elemID'), event,'jsf.ajax.request(\'elemID\',event,{render:\'LinkID\',onevent:renderAjax,\'javax.faces.behavior.event\':\'action\'})');

到目前为止它有效。但问题是我的 renderAjax 仍然是全局定义的。现在我将我的 JS 函数移动到一个需要 AMD 的模块中。函数 renderAjax 不再全局可用,我将 jsf ajax 代码也移动到我的模块中并访问它:

jsf.util.chain(document.getElementById('elemID'), event,'jsf.ajax.request(\'elemID\',event,{render:\'LinkID\',onevent:_t.renderAjax,\'javax.faces.behavior.event\':\'action\'})');

请注意,_t.renderAjax() 现在位于 Require JS 的一个模块中。我现在得到的错误是 _t 未定义。我猜它只希望看到全局函数。有趣的部分是它会自动围绕代码创建一个包装函数。有什么办法可以解决这个问题吗?

(function(event
/**/) {
jsf.ajax.request('elemID',event,{render:'LinkID',onevent:_t.renderAjax,'javax.faces.behavior.event':'action'})

})

如果有人能帮我解决这个问题,那将非常有帮助。

好的。我想通了。我不确定这是否是正确的方法。但它有效。

  1. 删除以下部分,因为创建虚拟 link 只是为了触发其上的点击事件(以使 JSF ajax 工作)是不正确的。它还强制定义全局处理程序回调,如上所述,这污染了全局命名空间,阻止了 JS 代码的模块化,使其无法迁移到 AMD:
<h:commandLink id="elemID" tabindex="-1" rendered="#{something.isEnabled ('showSomeLink')}">
    <f:ajax render="#{someBean.getElemId('LinkID')}" onevent="renderAjax" />
    <jsft:event type="ajax"> someBean.someMethod(); </jsft:event>
</h:commandLink>
  1. 现在创建一个名为 ajax.xhtml 的简单 XHTML 文件作为模板。

  2. 创建另一个名为 needHelp.xhtml 的文件,使用上述文件 ajax.xhtml 作为模板

步骤 (1) 和 (2) 均已显示在下图中: http://i61.tinypic.com/2rgypsi.png

  1. 在支持 bean 中,执行以下操作: http://i62.tinypic.com/1037bci.png

这样就完成了设置。我们的 Ajax 响应现在采用 JSON 格式并写入响应流。

现在,在所需的 AMD require 模块中,

define('checkChannel', ['jQuery', 'ajaxHandlers'], function($, ajaxHandlers){
  (function checkChannel(){
    $.ajax({
       url : baseURL+"/needHelp.jsf",
       dataType : "json",
       type: "POST",
       success : ajaxHandlers.renderAjax
    });
  })();
})

success 处理程序用于根据响应标志更新所需的任何 DOM 元素。在这种情况下,它将使用 id="LinkID" 更新元素,如原始代码片段中所定义。

现在,renderAjax 很好地封装在 AMD 模块中,不再需要全局定义。现在无需在 JSF 中使用 and 或 have dummy links created via 来模拟点击和触发 AJAX。如果视图状态需要维护,它总是可以以类似的方式获取和更新。

Also, baseURL is the basehref of your app. For example, if the page was www.example.com/tool/index.jsf, then the baseUrl would be www.example.com/tool/. So, the AJAX URL would be www.example.com/tool/needHelp.jsf

PS: pardon the external image links as Whosebug did not let me post pics / more links as i needed atleast 10 reputation to post. I had to use external images as I had some trouble formatting the code.