[修練營ASP.NET]先透過PageMethods撈DB,來決定按鈕是否要confirm訊息

  • 15142
  • 0
  • 2009-11-26

[修練營ASP.NET]先透過PageMethods撈DB,來決定按鈕是否要confirm訊息

前言

會寫這篇文章的起因來自於MSDN forum上的一個問題:用confirm中斷再繼續執行,updatepanel就不會更新


以前也有遇過這樣的需求,在針對敏感或重要的資料進行操作時,如果那些資料符合了某些條件,
則需要在畫面上顯示提示訊息,也就是confirm,讓User可以知道這筆資料的狀態,再來決定是否繼續執行動作。

這問題的麻煩之處,在於client端與server端事件順序,在這需求裡面會有點卡住。

confirm是javascript的語法,也就是要在頁面Render出來的時候,就該註冊上去的code。
但是我們的confirm,需要根據user在畫面輸入的資料,才能判斷是否需要顯示confirm訊息。

也就是「不能提早註冊confirm的script在button上」。

流程應該是,

  1. 點了按鈕後,去DB撈資料判斷是否要提示user,撈資料的部分code應該寫在server端。
  2. 如果要提示user,則client端的畫面上呈現confirm
  3. user點了「確定」,則執行該button應該執行的code。點了「取消」,則畫面維持原狀,不做任何處理。


由於confirm是client端的動作,
按照上面的作法就會變成下列步驟

  1. client端的button.onclientclick(這邊用onclientclick來區分是server事件還是client事件)
  2. postback
  3. server端的button.click(sender,e)
  4. 撈DB資料判斷是否要提示
  5. 註冊confirm訊息???????

問題的起源就卡在第5的步驟。

在button.click(sender,e)裡面註冊confirm到button上,已經太晚了,因為已經在server端的階段了,client端的處理在步驟1就已經結束了。
即便confirm出來了,按了確定,又要執行一次該按鈕在server端的code(例如存檔),再次執行server端的button.click(sender,e),可不能再去檢查一次DB資料 再confirm一次。

 

Suvery Solution

按照前言裡面的步驟,比較有經驗的developer,直覺想法可能是postback兩次,做好程式事件接口來處理這整個client->server->client->server的動作。
這邊就不貼出sample code,只講步驟跟概念:

  1. button上沒有client端事件要處理的事,把最後要存檔的function獨立出來一個private的function。
  2. 點了按鈕,postback,執行button.click(sender,e)
    • 到DB去撈資料,判斷是否要confirm
    • 是,註冊confirm和_dopostback(‘我的postback’,’用得到的DB訊息’)的script到「頁面」上
  3. 第二次postback
  4. confirm訊息,使用者點「確定」
  5. Page_load事件裡面判斷,此次的postback是否由「我的postback」所引起的,(透過eventTarget)
    • 是,則呼叫存檔的function


這樣的作法,的確可行,因為我們需要的是server->client->server,postback兩次的cycle是client->server->client->server
缺點是得postback兩次,
postback,當頁面一長,控制項一多,viewstate一肥,user頻寬太小,就很容易造成user需等待時間拉長。

有沒有可能不postback兩次呢?

直覺就是想到處理非同步的Ajax。
有了web service與ajax的PageMethods,很多東西都不需要postback來處理了。

於是有了想法,

在Button.onclientclick的時候,呼叫PageMethods,
去DB檢查是否要confirm,如果要,則呼叫confirm,
點了「確定」則不處理,繼續postback,執行server端的button.click(sender,e)
點了「取消」則return false,讓按鈕不postback。

直到我把上面的想法實做成sample code之後,我發現我錯了…

期望的順序是:

  1. client端的button.onclientclick,呼叫PageMethods
  2. PageMethods判斷完DB的資料,return bool
  3. bool若true,則執行client的confirm
  4. if confirm為false,則return false
  5. confirm為true,則執行server端的button.clieck(sender,e)

結果跑出來的順序不一樣:

  1. client端的button.onclientclick,呼叫PageMethods
  2. 執行server端的button.clieck(sender,e)
  3. PageMethods判斷完DB的資料,return bool
  4. bool若true,則執行client的confirm
  5. if confirm為false,則return false

client->PageMethods->server,變成了client->server->PageMethods。

這讓我頗為吃驚,跟原本想像的順序不太一樣。

但我還是很不甘願,決定加工把這樣的問題處理掉,基於PageMethods,還是要達到我們的目的。

 

Play it!

首先要使用PageMethos該有的設定,請參考:[ASP.NET AJAX]PageMethod:用javascript呼叫server端的method

概念都跟上面的期望順序一樣,
問題點只有在,怎麼樣讓server端的button.click(sender,e)在PageMethods後面執行。

加工的部分:

  1. 用一個hidden來存放PageMethods回傳的bool
  2. button.onclientclick呼叫一個js function檢查hidden的值,來決定要不要confirm
  3. 在textbox onchange事件就呼叫PageMethods。
  4. isPostBack為false的時候,呼叫PageMethods。
  5. 執行server端的button.click(sender,e)後要清掉hidden的值。

附上Sample code,撈資料的方式請自行修改,Sample Code上的方式是根據[修練營ASP.NET]使用Spring.Net輔助切層的專案架構 撈資料的。

.aspx


<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>未命名頁面</title>

    <script type="text/javascript">
    function CallMe(src,dest)
     {    
         var ctrl = document.getElementById(src);         
         PageMethods.IsDataExist(ctrl.value,CallSuccess,CallFailed,dest);
     }
 
     
     function CallSuccess(res, destCtrl)
     {    
         var dest = document.getElementById(destCtrl);         
         dest.value=res;         
     }
     
     
     function CallFailed(res, destCtrl)
     {
         alert(res.get_message());
     }
     
     function buttonclick()
     {
        if($get('HiddenField1').value=='true')
        {
            if(!confirm('已經存在資料,確定執行?'))
            {return false;}}
        else
        {return true;}
     }
    </script>

</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server"  EnablePageMethods="true">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                ID:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
                <asp:HiddenField ID="HiddenField1" runat="server" />
                <asp:Button ID="Button1" runat="server" Text="存檔" onclick="Button1_Click" OnClientClick="if(buttonclick()==false){return false;}" />
                <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

 

.cs


    protected void Page_Load(object sender, EventArgs e)
    {
        this.TextBox1.Attributes.Add("onchange", "CallMe('TextBox1','HiddenField1');");
        

        if (!IsPostBack)
        {
            this.TextBox1.Text = "2";
            ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "detectID", "CallMe('TextBox1','HiddenField1');", true);
        }
    }

    [System.Web.Services.WebMethod]
    public static bool IsDataExist(string RegionID)
    {
        Core.Domain.Interface.IRegion region=(Core.Domain.Region)Core.WebUtility.Repository.Domain("Region");
        
        if (RegionID.Length != 0)
        {
            region.Id = Convert.ToInt16(RegionID);
        }
        else
        { region.Id = null; }

        IList regionList = region.GetRegion();

        if (regionList.Count==0)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        this.TextBox2.Text = "存檔成功,ID為 "+this.TextBox1.Text;
        this.HiddenField1.Value = string.Empty;
        ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "detectID", "CallMe('TextBox1','HiddenField1');", true);
    }

畫面:(ID 1~4已經存在資料)

1.假設一開始textbox的值從DB撈出來assign給textbox。(測試沒有經過textbox的onchange事件)

1

2.點了存檔後,因為2已經存在DB,所以confirm。

2

3.點了取消,則畫面回到第一張圖。點了確定,則執行server端的button.click。

3

 

4.再點一次存檔按鈕,則仍顯示confirm,因為DB裡ID為2的資料仍然存在。
4

5.把ID改為5,點存檔,則沒有顯示confirm,直接執行server端的button.click(sender,e)

5

 

結論

這功能與需求其實並沒有什麼,Sample的情況也相當單純,
但是這個過程讓我發現了很多以前自己的觀念不一定是對的。

在面對與想像中不一樣而產生的bug時,再透過已知的觀念和技巧,去將bug fix掉,
再整理出來分享給大家,
希望大家的收穫可以比我還多。

補充相關連結:http://social.msdn.microsoft.com/Forums/zh-TW/236/thread/d9a39279-7626-4ce5-9cb8-df50d44c04ee


blog 與課程更新內容,請前往新站位置:http://tdd.best/