為了深入理解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缺失的一個環節,所以不大容易理解,需要大家認真對待。