当前位置: 代码迷 >> 综合 >> 整合 Ajaxanywhere
  详细解决方案

整合 Ajaxanywhere

热度:31   发布时间:2023-12-17 10:23:04.0

整合 Ajaxanywhere

原作地址:http://wiki.apache.org/myfaces/Integrating_Ajaxanywhere

翻译:ronaldo117

发布:Java Web Side http://www.js66.com/java),本文属于 JSF OpenDoc 系列,我们会努力贡献出更多更好的开放文档,来方便大家对 JSF 的学习和使用。

上周我听到一个我的用户提出的一个关于使用我的网页程序所产生缓慢性能的警告。首先,当我听到这个消息的时候我并不烦恼,然后我认为值得为这一个问题找原因做尝试。

变得忙碌起来!我来到那个人的书桌前并且和她讨论这个问题。对于我意识到一切事情都在不断的更新的事实,她给我说明了她的感觉就是网页刷新总是很缓慢;所以客户端和服务器段的响应时间并不是困扰她的真正原因,而是页面每次的刷新(她告诉我烦恼主要是闪烁效果)

然后一个想法在我的头脑中出现了“前几天我读到的一个框架怎么样,Ajaxanywhere”,我告诉我自己。想做就做,我下载了最新版本,这现在以 JSF 整合, 而且开始做一些测试。

我必须得承认我起先觉得很失望, 因为事实关于和 JSF 整合在网上缺乏资料 (只有一个基本的例子《每件事物是 " ajaxed")。这对于我的工作是远远不够的,所以我在论坛里面发了一些消息并且等待答复。后来一位Ajax anywhere的创作者Vitaliy Shevchuk给我指明了方向,而且最后我解决了我所有的问题,并且使它变得好像很有魔力一样的工作起来。

因此在这里我想要与你分享我的知识。(让你们不至于遇到问题时和我一样头痛)。步骤:

1) 下载 Ajaxanywhere 的最新版本。 (现在 1.1.0 .6) 。你应该知道这个jar包的META-INF文件夹下包含一个faces-config.xml文件(如果在你的应用程序中出现奇怪的错误, 检查该文件是不是在那里)

2) 在你应用程序的web.xml文件中编辑好路径打到Jar包中。

3)为你的过滤器类定义一个新的过滤器:这个过滤器就是一个封装了Servlet请求的一个对象。但是必须注意,你得申明使用哪一个过滤器工作,让我向你解释这个问题:

比如提交,Ajaxanywhere 使用一个 Servlet请求包裹器处理 AJAX 请求。如果混合使用其他的同样也封装了served请求的对象,就会引起冲突。我没有更深入地调查这一个问题,但是实事似乎是过滤器收到已经封装好的Servlet请求,而且调用写好的程序的时候,返回值是空。我发现问题是会使用由Myfaces继承的过滤器,(但是也许问题不是AAFilter, 是由于继承过滤器本身引起)容器会使用由Myfaces继承的过滤器之前使用定义好的过滤器。

4)当我使用MyfacesTiles的时候,我会解释我的特别的脚本,然后这就很容易挂接到你的脚本里面去了。

打开你的根目录的模板。我的是由四个部分组成:表头, 左边的菜单,中心和注脚。在我的例子里面,每个请求都是在中心区域改变(这个区域是动态生成的,我想你应该知道我在说什么,Ajax)因此我决定 " ajaxifize" 中心区域。看一看这一个代码片断:

<f:facet name="body">

<f:subview id="bodySubview">

<aa:zoneJSF id="ajaxZone">

<htm:script type="text/javascript" src="aa/aa.js" />

<tiles:insert attribute="body" flush="false" />

<deltar:messages alertJavascript="true" showDetail="false" />

</aa:zoneJSF>

</f:subview>

</f:facet>

就像你看到的那样,你需要指明这个aa.js文件在什么地方得到你的“ajaxed”页面:这个js文件是在这个内容的表头。他创建了一个Ajaxanywhere对象,然后他就会非常神奇的替换需要所有AJAX请求的结果。

然后, ZoneJSF 附签将会让你作标记那“要被ajaxd的”区域,因此 Ajaxanywhere 能自己识别它。

一旦应用程序的中心区域已经被作标记 " ajaxed", 现在是该为每个页面提供特别的信息时候(当然你并不是要想整个页面都"ajaxify"刷新)

5)把一个这样的ID赋给你的HTML窗体--> <h:form id="ajaxedForm" .... >(值得注意的是如果这个窗体在其他的组件里面,我们应该象ID1:ID2:ID3……这样依次编号 -我们不能为窗体使用forceID标签)

6) 万一你只想要一些按钮作一个 AJAX 请求, 也给他们一个ID(这种情况是你能在引用他们之后使用 forceId 属性)--><t:commandButton id="ajaxButtonSave" ... />

7) 在页的最结束, 插入一个 JS 代码告知Ajaxanywhere你在什么地方使用AJAX请求。让我们看一看一段js代码(顺便提一下,我使用的是JSF手册的HTMLin - 一个修正版)

<htm:script type="text/javascript">

<t:outputText value="<!--

ajaxAnywhere.getZonesToReload = function(url, submitButton){

if (typeof submitButton != 'undefined') {

if (submitButton.type == 'submit') {

if (submitButton.id.search('ajaxButton') != -1) {

return 'ajaxZone';

}

}

}

return null;

}

var elements = new Array();

elements.push(getElmById('ajaxButtonSave'));

ajaxAnywhere.formName = 'bodySubview :mainForm'; ajaxAnywhere.substituteFormSubmitFunction(); ajaxAnywhere.substituteSubmitButtonsBehavior(true, elements); -->" />

</htm:script>

首先注意这个回调的getZonesToReload这个部分。这里你必须提交按钮告诉Ajaxanywhere加载区域的一些基本信息。在这个例子中,当你的ID"ajaxButton"的提交按钮发生单击事件时你告诉了Ajaxanywhere装载区域的名字“ajaxZone(一个小窍门就是你不用为页面的每一个提交按钮都指定)

如果你不为Ajaxanywhere指定角色,那么他的返回值是空。

然后,接下来你能找到一些正确的继承于Ajaxanywhere的被Ajaxanywhere处理的提交按钮的信息。我遇到一些问题是因为我没有验证过数组按钮的substituteSubmitButtonsBehaviour函数(我推想不使用函数中的变量会导致所有的按钮被考虑)。但是我现在还不知道这是我的程序的问题呢还是使用了AAFilter后的副作用(参看上面)

如果你要求象我的脚本这样,每个都"ajaxed",提交按钮遵从数据滚动连接,那么只是总是返回"ajaxZone";但是如果你还是仍然想使用数组按钮作为substituteSubmitButtonsBehavior(真实的,组件)

ajaxAnywhere.getZonesToReload = function(url, submitButton) {

return 'ajaxZone';

}

8)检查你的jsf的导航配置,修改配置文件的位置为Ajaxanywhere(如果正确的话,那么一个正常的sendRedirect请求将会被处理,并且释放你所有的"ajaxification") .

9)仅仅为了测试,如果每个事件都想象这样工作的的话,将下面这个Ajaxanywhere例子的js的码段添加到你的主页面:

<span id=cnt>0</span> seconds since last page refresh. <script>

var sec=0; function counter(){

setTimeout("counter();",1000); document.getElementById("cnt").innerHTML = sec++;

} counter();

</script>

就像你看到的那样,这个JS代码段是在你的每一个页面的顶部添加了一个计数器,当页面刷新的时候你能看见(但是并不会表现出来)

因此连同 Myfaces 一起,当与 Ajaxanywhere 合作的时候,这是拿的所有的步骤。注意你将会对那些使用者的多点一下 [返回]按钮的这类令人感到懊恼的问题说 goodbye
原文

Last week I was warned by one of my users about the slow performance of web pages generated by my application. At first, I got annoyed by this comment, but then I thought that it was worth the effort trying to find the cause for this problem.

 

Get busy! I went to this person's desk and asked her about the problem. Once she explained me her feelings about the application I realized that everything was due to the fact that the page was being always completely refreshed; so the response time between the client and the server was not really the problem disturbing her, but the page being refreshed every time (with the annoying flicker effect, as she told me).

 

Then, a lightbulb turned on over my head... "What about that framework I read the other day... Ajaxanywhere, I think" - I told to myself. Thought and done. I downloaded the latest version, which now integrates with JSF, and began to do some testing.

 

I must admit I really got frustrated at first, due to the fact that there's a lack of documentation in the web regarding the integration with JSF (only a basic example where everything is "ajaxed"). I was not able to make it work, and I posted several messages in the forum to guess what was happening. Answers from other users and from one of Ajaxanywhere's creator, Vitaliy Shevchuk, pointed me the direction to go... and I finally solved all my problems and made it work like a charm!

 

So here I want to share my knowledge with you (so you can save some headaches as I couldn't do). Steps to take:

 

1) Download latest version of Ajaxanywhere (currently 1.1.0 .6). This jar has a faces-config.xml file inside the META-INF folder which you must know about (in case strange errors appear in your application, check it is there).

 

2) Put it the JAR in your application's classpath and edit web.xml

 

3) Define a new filter in your filter chain for the AAFilter class: this filter is a wrapper for the ServletResponse object. But be careful, the order in which you declare this filter will make it work or not. Let me explain you the problem:

 

As comented, Ajaxanywhere uses a ServletResponse wrapper to handle AJAX requests. If used in combination with other web filters which also wrap the ServletResponse object, it causes conflicts. I haven't investigated deeper this problem, but it seems it has to be with the fact that the AAFilter receives the already wrapped ServletResponse, and sometimes calls its writer, which is null when being wrapped. I detected this problem with the Extension filter used by Myfaces (but maybe the problem is not with the AAFilter, but with the ExtensionFilter itself). The solution is just to define the AAFilter before the Myfaces extension filter, and vualà!

 

4) As I'm using Tiles with Myfaces, I will explain my particular scenario, which can be easily extrapolated to your particular scenario.

 

Take your root template. Mine is a typical one made of 4 sections: header, left menu, center, and footer. In my case, only the center zone changes on every request (obviously all zones are dynamically generated, but you know what I'm saying). So I decided to "ajaxifize" the center zone. Take a look at this code snippet:

 

<f:facet name="body">

 

<f:subview id="bodySubview">

 

<aa:zoneJSF id="ajaxZone">

 

<htm:script type="text/javascript" src="aa/aa.js" /> <tiles:insert attribute="body" flush="false" /> <deltar:messages alertJavascript="true" showDetail="false" />

 

</aa:zoneJSF>

 

</f:subview>

 

</f:facet>

 

As you can see, you will need to indicate the aa.js file somewhere available to each of your "ajaxed" pages: this JS file is the heart of the matter. It creates the Ajaxanywhere object, and does all the replacing needed to capture AJAX requests which results in all this "magic".

 

Then, the ZoneJSF tag will allow you to mark the "to-be-ajaxed" zone, so Ajaxanywhere can know about it.

 

Once the whole center zone of the application has been marked as "ajaxed", it's time to give the particular information for each page (as maybe not always you want to "ajaxify" the whole page 

 

5) Always give an ID to your HTML form --> <h:form id="ajaxedForm" .... > (notice that if this form is within another component, you should give the complete ID in the terms of id1:id2:id3... - we don't have the forceId for the form tag)

 

6) In case you only want some buttons to make an AJAX request, give them also an ID (in this case, yes, you can use the forceId attribute, which help to reference them after) --> <t:commandButton id="ajaxButtonSave" ... />

 

7) At the very end of the page, insert a piece of JS code to inform Ajaxanywhere about your intentions with AJAX requests. Let's see a snipped JS code (by the way, I use HTMLin from JSF Tutorials - a modified version by me indeed):

 

<htm:script type="text/javascript">

 

<t:outputText

 

value="<!--

 

ajaxAnywhere.getZonesToReload = function(url, submitButton) {

 

if (typeof submitButton != 'undefined') {

 

if (submitButton.type == 'submit') {

 

if (submitButton.id.search('ajaxButton') != -1) {

 

return 'ajaxZone';

 

}

 

}

 

}

 

return null;

 

}

 

var elements = new Array(); elements.push(getElmById('ajaxButtonSave'));

 

ajaxAnywhere.formName = 'bodySubview :mainForm'; ajaxAnywhere.substituteFormSubmitFunction(); ajaxAnywhere.substituteSubmitButtonsBehavior(true, elements); -->" />

 

</htm:script>

 

First notice the getZonesToReload callback. Here you must tell Ajaxanywhere what zone to reload (if any) based on information of the submitted button. In this example, you tell Ajaxanywhere to reload the zone named "ajaxZone", just in case a submit button which ID starts with "ajaxButton" is clicked (a little trick not to have to ask for every submit button in your page).

 

If you don't want to Ajaxanywhere to do its role, the simply return null.

 

Then, below you find some extended information for Ajaxanywhere about the exact submit buttons you want to be processed by Ajaxanwhere. I experienced some problems here because I was not passing the array of buttons to the substituteSubmitButtonsBehaviour function (I supposed that not using that argument would cause all the buttons to be considered). But now I'm not sure whether this was really a problem, or a side-effect of the problem I had with the AAFilter (see above).

 

If you, like in my scenario, want evertyhing to be "ajaxed", from submit buttons to data scroller links, then simply return always "ajaxZone"; but you'll have to continue using the buttons array for the substituteSubmitButtonsBehavior(true, elements):

 

ajaxAnywhere.getZonesToReload = function(url, submitButton) {

 

return 'ajaxZone';

 

}

 

8) Review your navigation configuration for JSF, as no redirects are allowed for Ajaxanywhere (if it finds them, then a normal sendRedirect request will be handled, loosing all your "ajaxification".

 

9) Just for the sake of testing if everything's working, add to your main root page, this JS code snippet taken from the examples given by Ajaxanywhere:

 

<span id=cnt>0</span> seconds since last page refresh. <script>

 

var sec=0; function counter(){

 

setTimeout("counter();",1000); document.getElementById("cnt").innerHTML = sec++;

 

} counter();

 

</script>

 

As can be seen, this JS code snippet will show a counter on top of every of your pages, so you can see when the page gets really refreshed (it shouldn't be never from now!!!!!!).

 

So this are all the steps to take when working with Ajaxanywhere together with Myfaces. Notice that you will say goodbay to those annoying problems regarding the user clicking the back button once and again