[原创]ExtAspNet秘密花园(十) — Ajax特性
为了深入理解ExtAspNet的Ajax特性,我们还是先来看看Asp.Net中的回发机制,继而讲解ExtAspNet是如何对此回发过程进行改造,来实现ExtAspNet所特有的原色Ajax。
Asp.Net的回发机制
简单说来,Asp.Net的回发过程是这样:首先服务器将服务器端控件渲染为HTML返回给浏览器,当用户进行某项操作时,通过表单提交的方式将控件相关数据提交到服务器,然后在服务器端恢复控件状态并触发相应事件。
在Asp.Net中有两种表单的提交方式:
1. 通过type="submit"的默认表单提交
下面来看一个简单的示例:
ASPX页面:
1: <form id="form1" runat="server">
2: <asp:Button ID="Button1" runat="server" Text="Button1" OnClick="Button1_Click">
3: </asp:Button>
4: </form>
1: <html>
2:
3: <head>
4: <title></title>
5: </head>
6:
7: <body>
8: <form name="form1" method="post" action="textbox.aspx" id="form1">
9: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0…6y+e3Tou1Yo="
10: />
11: <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgL…PagKn2HgW"
12: />
13: <input type="submit" name="Button1" value="Button1" id="Button1" />
14: </form>
15: </body>
16:
17: </html>
点击Button1提交表单时,HTTP请求参数如下所示:
回顾整个过程,表单提交是通过点击提交按钮(type=submit)触发的,在HTTP请求参数中我们发现Button1=Button1,这个参数是浏览器的默认添加的。服务器端也正是根据这个参数来找到点击了那个控件,并触发相应的服务器端事件的。
2. 通过JavaScript控制的表单提交
我们对上面的示例稍作修改:
ASPX页面:
1: <form id="form1" runat="server">
2: <asp:Button ID="Button1" runat="server" Text="Button1" UseSubmitBehavior="false" OnClick="Button1_Click">
3: </asp:Button>
4: </form>
生成的HTML标签为:
1: <html>
2: <head><title></title></head>
3:
4: <body>
5: <form name="form1" method="post" action="textbox.aspx" id="form1">
6:
7: <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
8: <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
9: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0…e3Tou1Yo=" />
10:
11: <script type="text/javascript">
12: //<![CDATA[
13: var theForm = document.forms['form1'];
14: if (!theForm) {
15: theForm = document.form1;
16: }
17: function __doPostBack(eventTarget, eventArgument) {
18: if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
19: theForm.__EVENTTARGET.value = eventTarget;
20: theForm.__EVENTARGUMENT.value = eventArgument;
21: theForm.submit();
22: }
23: }
24: //]]>
25: </script>
26:
27: <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgLlxsf1…wCPagKn2HgW" />
28:
29: <input type="button" name="Button1" value="Button1" onclick="javascript:__doPostBack('Button1','')" id="Button1" />
30: </form>
31: </body>
32: </html>
点击Button1提交表单时,HTTP请求参数如下所示:
虽然两次得到的结果一致,但是第二个示例是通过JavaScript来提交表单的。服务器端根据HTTP请求中__EVENTTARGET的值来找到回发的控件,并触发相应控件的服务器端事件。
ExtAspNet特有的Ajax处理机制
我们都知道ExtAspNet的所有控件行为都是默认AJAX的,那个ExtAspNet是如何做到的呢?
其实道理也简单,你可以打开ExtAspNet的源代码,找到js/X/X.ajax.js文件。我们会发现如下的代码:
1: X.ajax = {
2: hookPostBack: function () {
3: if (typeof (__doPostBack) != 'undefined') {
4: __doPostBack = x__doPostBack;
5: }
6: }
7: };
8:
9: function x__doPostBack(eventTarget, eventArgument) {
10: if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
11: theForm.__EVENTTARGET.value = eventTarget;
12: theForm.__EVENTARGUMENT.value = eventArgument;
13:
14: Ext.Ajax.request({
15: form: theForm.id,
16: url: document.location.href,
17: success: function (data) {
18: // ....
19: },
20: failure: function (data) {
21: // ...
22: }
23: });
24: }
25: }
在页面初始化时,我们会调用X.ajax.hookPostBack,此函数内使用x__doPostBack函数来覆盖页面生成的__doPostBack函数,这样原本所以对__doPostBack的调用都进入我们自己定义的处理函数中。在x__doPostBack处理函数中,我们不是调用表单的submit函数,而是调用Ext.Ajax.request来发起一次Ajax请求。
简单来说,就是首先拦截__doPostBack函数,然后调用Ext.Ajax.request,是不是很简单。
问题来了
我们介绍了Asp.Net的两种回发机制,其中第二种使用的正是__doPostBack函数,但是第一种却依赖于浏览器对type=submit的默认处理行为。这也导致在页面中使用正常Asp.Net的Button,不会有ExtAspNet所特有的AJAX行为,怎么办?
由于我们无法修改Asp.Net控件的实现代码,也不可能向每个使用者传授UseSubmitBehavior=false的小技巧,我们希望使用ExtAspNet的开发者能够不假思索地快速开发,而不是陷在Asp.Net所设置的陷阱中。怎么办?
迎刃而解
困难是难不倒我们这些勤劳善良的程序员,让我来略施小技:
既然无法控制第一种提交方式,我们就来将其改造为第二种方式怎么样:
1: makeAspnetSubmitButtonAjax: function (buttonId) {
2: function resetButton(button) {
3: button.set({
4: "type": "button"
5: });
6: button.addListener("click", function (event, el) {
7: __doPostBack(el.getAttribute("name"), "");
8: event.stopEvent();
9: });
10: }
11:
12: if (typeof (buttonId) === "undefined") {
13: Ext.each(Ext.DomQuery.select("input[type=submit]"), function (item, index) {
14: resetButton(Ext.get(item));
15: });
16: } else {
17: var button = Ext.get(buttonId);
18: if (button.getAttribute("type") === "submit") {
19: resetButton(button);
20: }
21: }
22: }
同时ExtAspNet还提供了一个参数来关闭这种行为,它就是PageManager的EnableAspnetSubmitButtonAjax属性。
从这个小地方也体现了ExtAspNet细致入微、精益求精的态度,致力为广大开发者打造一个方便快捷的Asp.Net控件库。
ExtAspNet和AspNet的按钮同时出现的示例
你可以在线查看这个示例的运行效果,其界面截图如下所示:
ASPX的页面标签非常清晰:
1: <ext:PageManager ID="PageManager1" AjaxAspnetControls="aspBox,aspButton" runat="server" />
2: <ext:ContentPanel ID="ContentPanel1" runat="server" Width="500px" BodyPadding="5px" EnableBackgroundColor="true" ShowBorder="true" ShowHeader="true" Title="内容面板">
3: <ext:TextBox runat="server" Width="300px" ID="extBox"></ext:TextBox>
4: <asp:TextBox runat="server" Width="300px" ID="aspBox"></asp:TextBox>
5: <ext:Button ID="extButton" runat="server" CssClass="inline"
6: Text="ExtAsp.Net 按钮" OnClick="extButton_Click"></ext:Button>
7: <asp:Button ID="aspButton" Text="Asp.Net 按钮" runat="server"
8: OnClick="aspButton_Click" />
9: </ext:ContentPanel>
这里有两个应用技巧:
1. 除非放在最外层的<form>标签里,否则Asp.Net的标签必须放在ContentPanel中。
2. 通过AjaxAspnetControls属性来定义需要在回发是更新的Asp.Net控件,这个特性在《ExtAspNet秘密花园(四) — 每个页面需要一个PageManager》有详细的描述。
后端的C#代码没有任何特殊的地方:
1: protected void extButton_Click(object sender, EventArgs e) {
2: aspBox.Text = "Asp.Net 输入框 - " + DateTime.Now.ToLongTimeString();
3: extBox.Text = "ExtAsp.Net 输入框 - " + DateTime.Now.ToLongTimeString();
4: aspButton.Text = "Asp.Net 按钮 - " + DateTime.Now.ToLongTimeString();
5: extButton.Text = "ExtAsp.Net 按钮 - " + DateTime.Now.ToLongTimeString();
6: }
7:
8: protected void aspButton_Click(object sender, EventArgs e) {
9: aspBox.Text = "Asp.Net 输入框 - " + DateTime.Now.ToLongTimeString();
10: extBox.Text = "ExtAsp.Net 输入框 - " + DateTime.Now.ToLongTimeString();
11: aspButton.Text = "Asp.Net 按钮 - " + DateTime.Now.ToLongTimeString();
12: extButton.Text = "ExtAsp.Net 按钮 - " + DateTime.Now.ToLongTimeString();
13: }
至此,整个示例就完成。没有任何让人感到突兀的地方,而Asp.Net的按钮控件却自动拥有了ExtAspNet的AJAX特性。
小结
本章可以明显看出ExtAspNet的对细节的追求和微创新,首先是对__doPostBack函数的拦截来实现Ajax,从而保证Asp.Net的默认调用方式不变,也为方便的切换Ajax与传统的页面回发埋下了伏笔;其次对于type=submit的按钮,ExtAspNet通过PageManager的EnableAspnetSubmitButtonAjax属性来将其转化为第一种方式,从而保证了ExtAspNet的Ajax在所有情况的通用性。
下一篇文章,我们开始讲述页面中布局的使用,布局是用好ExtAspNet的关键,也是Asp.Net缺失的一个环节,所以不大容易理解,需要大家认真对待。