[原创]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>
生成的HTML标签为:
   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请求参数如下所示:

image

 

回顾整个过程,表单提交是通过点击提交按钮(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请求参数如下所示:

image

 

虽然两次得到的结果一致,但是第二个示例是通过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的按钮同时出现的示例

你可以在线查看这个示例的运行效果,其界面截图如下所示:

image

 

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缺失的一个环节,所以不大容易理解,需要大家认真对待。

 

注:《ExtAspNet秘密花园》系列文章由三生石上原创,博客园首发,转载请注明出处。文章目录 官方论坛

posted @ 2012-03-04 15:14 三生石上(FineUI控件) 阅读(...) 评论(...) 编辑 收藏