使用IPostBackEventHandler讓JavaScript“呼叫”回傳事件

謙行發表於2013-09-28

由ASP.NET所謂前臺呼叫後臺、後臺呼叫前臺想到HTTP——實踐篇(二)通過自己模擬HTML標籤事件與伺服器互動,講了ASP.NET的伺服器控制元件是怎麼render成HTML後市怎麼“呼叫”後臺方法的,有同學看了後問了我個問題:你講的方式確實可以,但我遇到的問題時這樣的,我想讓自己寫的DIV點選一下提交表單,我是自己寫post好呢還是用頁面上的__doPostBack方法好呢?

我想了一下,覺得都不好。若是用自己寫隱藏域,然後賦值提交的方法,原理雖然正確,但我們需要做很多額外工作;如果呼叫頁面上自動生成的__doPostBack,萬一頁面上沒有伺服器控制元件,那麼頁面頁面上也就不會有這個方法了,而且並不是所有的伺服器控制元件都生成這個方法,退一萬步,要是微軟改介面,方法名字變了怎麼辦?

廢話了半天,我們因該怎麼處理這種情況呢?正如預期,微軟又替我們想好了,IPostBackEventHandler介面就是做這事兒的,看個例子

<form id="form1" runat="server">
        <div id="divTest" onclick="clientPostback(this);">Click to Post Back</div>

        <script type="text/javascript">
            function clientPostback(obj) {
                setTimeout(postBackClientHandler.replace(/arg_placeholder/g,obj.id), 0);
            }
        </script>
    </form>

頁面上有個DIV,點選的時候呼叫JavaScript clientPostBack方法,我們知道所謂JavaScript呼叫後臺都是通過表單提交的方式實現的,在clientPostBack中有條奇怪的語句, setTimeout(postBackClientHandler.replace(/arg_placeholder/g,obj.id), 0); ,看看後臺程式碼,就明白這是什麼了

public partial class Default : System.Web.UI.Page,IPostBackEventHandler
    {

        protected override void OnPreRender(EventArgs e)
        {
            PostBackOptions pbo = new PostBackOptions(this, "arg_placeholder", "", false, false, false, true, false, "");
            string clientScript = Page.ClientScript.GetPostBackEventReference(pbo);
            string postBackClientHandler = string.Format("\nvar postBackClientHandler=\"{0}\";\n", clientScript);
            Page.ClientScript.RegisterStartupScript(typeof(Default), "postBackClientHandler", postBackClientHandler, true);
            base.OnPreRender(e);
        }

        public void RaisePostBackEvent(string eventArgument)
        {
            Response.Write("Event argument is " + eventArgument);
        }
    }

首先讓類實現IPostBackEventHandler介面,實現RaisePostBackEvent方法,MSDN上市這麼解釋這個方法的:當由類實現時,使伺服器控制元件能夠處理將窗體傳送到伺服器時引發的事件。這個方法就是JavaScript 提交表單後.NET自動執行的方法,我們把頁面傳來的引數輸出。

在頁面的PreRender事件處理程式中,使用PostBackOptions物件建立並註冊客戶端提交表單所用的指令碼,想看明白這段程式碼,首先得了解PostBackOptions物件。MSDN上這麼解釋PostBackOptions:指定如何生成客戶端 JavaScript 以啟動回發事件。看看這個物件建構函式的幾個引數

引數
targetControl
    型別:System.Web.UI.Control
         用於接收回發事件的 Control
argument
    型別:System.String
        在回發事件期間傳遞的可選引數。
actionUrl
    型別:System.String
        回發的目標。
autoPostBack
    型別:System.Boolean
        如果需要響應使用者操作而自動將窗體回發到伺服器,則為 true;否則為 false。
requiresJavaScriptProtocol
    型別:System.Boolean
        如果 javascript: 字首是必需的,則為 true;否則為 false。
trackFocus
    型別:System.Boolean
        如果回發事件應將頁返回到當前的滾動位置並將焦點返回到目標控制元件,則為 true;否則為 false。
clientSubmit
    型別:System.Boolean
         如果回發事件可以由客戶端指令碼引發,則為 true;否則為 false。
performValidation
    型別:System.Boolean
         如果在回發事件發生之前要求在客戶端進行驗證,則為 true;否則為 false。
validationGroup
    型別:System.String
         一個控制元件組,當該控制元件組回發到伺服器時,PostBackOptions將引發對它的驗證。

程式碼中 string clientScript = Page.ClientScript.GetPostBackEventReference(pbo); 是整個註冊的核心,通過這條語句就可以獲取註冊到客戶端的指令碼語句,然後註冊到頁面。看看頁面生成的程式碼

<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
//]]>
</script>
<script type="text/javascript">
//<![CDATA[

var postBackClientHandler="__doPostBack('__Page','arg_placeholder')";
//]]>
</script>

通過程式碼分歧可以看出string clientScript = Page.ClientScript.GetPostBackEventReference(pbo); 語句得到的結果是 __doPostBack('__Page','arg_placeholder')。我們通過string postBackClientHandler = string.Format("\nvar postBackClientHandler=\"{0}\";\n", clientScript); 語句將其包裝成一個字串變數註冊到頁面,否則它是一個函式呼叫語句,頁面會不停的被提交(有興趣同學可以試試將其直接註冊到頁面)。

這樣回頭看看客戶端DIV onclick方法是什麼意思

postBackClientHandler是字串”__doPostBack('__Page','arg_placeholder')”

postBackClientHandler.replace(/arg_placeholder/g,obj.id) 這條語句是把字串中的”arg_placeholder”替換為我們希望傳遞給伺服器的引數,也就是通過引數隱藏域提交到伺服器的資料,這樣我們寫的客戶端方法實際上是這樣

function clientPostback(obj) {
                setTimeout("__doPostBack('__Page','divTest')", 0);
            }

至於setTimeout是因為我們得到是一個字串而不是JavaScript語句,使用setTimeout和eval效果類似,將字串轉為可執行語句,這時是不是既方便又可靠了呢。

相關文章