2012年8月16日 星期四

在 UpdatePanel 裡面使用 javascript

近日在使用 Asp.Net 所提供的 Ajax 功能時,對於 UpdatePanel 與 JavaScript 之間的愛恨糾葛,還是跳出來說幾句話好了。

從自己最常使用的功能說起。在網頁上放個 Button,壓下去後,跳出一個 alert 視窗,程式如下:
<form id="form1" runat="server">
<div>
    <asp:button id="Button1" runat="server" onclick="Button1_Click" text="Button" />
</div>
</form>
以及 .cs 程式
protected void Button1_Click(object sender, EventArgs e)
{
    ClientScript.RegisterStartupScript(this.GetType(), "JustAlert", 
        "<script> alert('Hello');</script>");
}
但當我將這個 button 移到 UpdatePanel 裡,就開始不正常了。我的 javascript 完全沒有反應,也沒有錯誤訊息。詳閱了「RegisterStartupScript Method (control, type, key, script, addScriptTags)」後,發現程式需要做些調整。該文章有提到,如果是在 UpdatePanel 裡,當我們使用了非同步更新的方法,要改用 ScriptManager 的 RegisterStartupScript,如下所示:

protected void Button1_Click(object sender, EventArgs e)
{
    ScriptManager.RegisterStartupScript(this.Page, this.GetType(),
        "JustAlert", "alert('Hello');", true);
}

既然在 UpdatePanel 裡需改用 ScriptManager 來呼叫 JavaScript,那理論上我在後端也可以呼叫前端所定義的 JavaScript 囉!所以,再試了一下:
/* .aspx  */
<head runat="server">
    <title></title>
    <script>
        function SayHi() {
            alert('Hi');
        }
    </script>
</head>

/* .cs */
ScriptManager.RegisterStartupScript(this.Page, this.GetType(),
    "JustAlert", "SayHi();", true);
果然可行。原來呼叫前端的 javascript 都沒問題,那是否也可以在 UpdatePanel 裡觸發整個頁面的 PostBack 呢?
ScriptManager.RegisterStartupScript(this.Page, this.GetType(),
    "JustAlert", string.Format("__doPostBack('{0}','');", this.ClientID), true);
測試後,還蠻好用的,能夠順利的讓整個頁面都 PostBack。

這讓我想到一個情境。如果我的頁面很複雜很複雜,其中會有一個按鈕。當使用者按下這個按鈕後,會在後端先去做初步的判斷,決定是否要進一步去執行後續的動作,如果判斷沒必要繼續往後執行,則會在前端跳出訊息。但因為畫面很複雜很複雜(想像一下嘛,總有一天你會遇到),當被判斷沒必要往後執行時,也不希望造成整個頁面的 PostBack 而重載所有物件。

這情境下,我可以把確認按鈕,放到 UpdatePanel 裡面,並在 Button 事件裡,先去判斷是否有必要繼續往後執行。如果有必要,就會執行上面所說的,在 UpdatePanel 裡面觸發前端的 __doPostBack() 來達到頁面 PostBack 的效果。相反地,如果判斷沒有必要往後執行,則是透過 ScriptManager.RegisterStartupScript() 來產生前端 javascript 的 alert( ) 效果。

以下是測試的範例程式:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>   
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>   
        <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
        <div>
        <img src="https://encrypted-tbn2.google.com/images?q=tbn:ANd9GcSloWls9-zDJUF2RvunIn3-5zB8HJ-PTGJ3tc_jXIMJcbxBDvu7DQ" border="0"></img>

        </div>
        <asp:RadioButtonList ID="rbl" runat="server" 
            RepeatDirection="Horizontal">
            <asp:ListItem Value="1">未滿18歲</asp:ListItem>
            <asp:ListItem Value="2">滿 18 歲</asp:ListItem>
        </asp:RadioButtonList>

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="確認" />
            </ContentTemplate>
        </asp:UpdatePanel>

    
    </div>
    </form>
</body>

public partial class ChangeMsgShow4 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //取得是誰觸發了 PostBack
        string strTarget = "" + Request.Form["__EVENTTARGET"];
        //判斷觸發 PostBack 的物件名稱是否為 MySpecial
        if (strTarget == "MySpecial")
        {
            //執行自訂的後續執行方法
            DoSpecial();
        }
    }

    private void DoSpecial()
    {
        Label1.Text = "看蜘蛛人有這麼麻煩嗎?";
    }

    protected void Button1_Click(object sender, EventArgs e)
    { 
        switch(rbl.SelectedValue)
        {
            case "1":
                //判斷不需要繼續往後執行,所以只在前端 alert 一些訊息
                ScriptManager.RegisterStartupScript(this.Page, this.GetType(),
                    "JustAlert", "alert('孩子,等待是值得的!');", true);
                break;

            case "2":
                //判斷後有必要往後執行,所以在前端先跳出一段訊息後,接著觸發 __doPostBack,並
                //把觸發 PostBack 的物件來源名稱設定為 MySpecial
                ScriptManager.RegisterStartupScript(this.Page, this.GetType(),
                        "JustAlert", "alert('恭喜你已經成年!'); __doPostBack('MySpecial','');", true);
                break;

            default:
                //判斷不需要繼續往後執行,所以只在前端 alert 一些訊息
                ScriptManager.RegisterStartupScript(this.Page, this.GetType(),
                    "JustAlert", "alert('請摸著良心選擇!');", true);
                break;

        }
    }
}

以前 Asp.Net 的前端與後端,總像是牛郎織女一樣,久久才會見面一次。但透過 Ajax 的技術,讓這兩人隨時都可以明來暗去,樂鬧許多。
參考:
01:RegisterStartupScript Method (control, type, key, script, addScriptTags)

沒有留言:

張貼留言