今天同事剛好問到如何透過AJAX方式在表單送出前進行資料檢核,索性把我的做法整理一下,供大家參考指教。

先用一個非AJAX,純Javascript的範例開始說明。

假設有一個ASP.NET網頁,txtCode輸入文字,btnSubmit送出表單,用jQuery在btnSubmit掛上onClick事件,檢查txtCode是否為"Darkthread": 若否就alert("Invalid!");若是則return true允許送出表單。程式很單簡單,在此就不再多做說明,請大家自行參考以下程式碼: (txtCode前方我放了一個Plurk跳舞香蕉人當伏筆)

<%@ Page Language="C#" %>
<script runat="server">
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        Response.Write(txtCode.Text);
        Response.End();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Async Call</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.js" 
            type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            function validate() {
                if ($("#txtCode").val() == "Darkthread")
                    return true;
                else
                    return false;
            }
            $("#btnSubmit").click(function () {
                if (!validate()) {
                    alert("Invalid!");
                    return false;
                }
            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <img src="http://statics.plurk.com/a55bdb344892676b0fea545354654a49.gif" />
    <asp:TextBox ID="txtCode" runat="server"></asp:TextBox>
    <asp:Button ID="btnSubmit" runat="server" Text="Submit" 
        onclick="btnSubmit_Click" />
    </form>
</body>
</html>

下一步,假設檢查邏輯很複雜,涉及資料庫查詢,故只能在Server端進行。我們將其修改為AJAX版,在Page_Load中加入邏輯,以Request["m"] == "validate"為觸發條件,透過Request["t"]取得使用者輸入文字,依文字是否為"Darkthread"決定傳回"OK"或"FAIL"。

再來看前端的Script要怎麼寫,首先示範懶人寫法:

由於btnSubmit在click事件需等待AJAX呼叫結果才能決定return true或false,所以我們做點小手腳,捨棄$.get()或$.post()不用,改用$.ajax(),借用其中一個參數: async,很沒出息地把它設成false(預設為true,$.get()/$.post()時也都被設為true)... 這樣有個好處: $.ajax()執行時,程式會卡住直到AJAX呼叫的結果傳回為止,程式邏輯可以一氣喝成,跟先前寫法一樣單純:

<%@ Page Language="C#" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request["m"] == "validate")
        {
            //System.Threading.Thread.Sleep(5000);
            Response.Write(Request["t"] == "Darkthread" ? "OK" : "FAIL");
            Response.End();
        }
    }
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        Response.Write(txtCode.Text);
        Response.End();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Async Call</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.js" 
            type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            function validate() {
                var res = "";
                $.ajax({
                    url: "Test.aspx", 
                    data: { m: "validate", t: $("#txtCode").val() },
                    success: function (result) { res = result; },
                    cache: false,
                    async: false
                });
                return res == "OK";
            }
            $("#btnSubmit").click(function () {
                if (!validate()) {
                    alert("Invalid!");
                    return false;
                }
            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <img src="http://statics.plurk.com/a55bdb344892676b0fea545354654a49.gif" />
    <asp:TextBox ID="txtCode" runat="server"></asp:TextBox>
    <asp:Button ID="btnSubmit" runat="server" Text="Submit" 
        onclick="btnSubmit_Click" />
    </form>
</body>
</html>

這個做法之所以沒出息,是因為同步式呼叫(async: false)將會中斷網頁執行,等待結果的過程中,網頁將被凍結。試著將Page_Load裡Thread.Sleep(5000)的註解拿掉,就可以借香蕉人突顯這個弱點。在等待的五秒裡,香蕉人僵住定格,不再跳舞,整個網頁也對使用者的滑鼠、鍵盤毫無反應。基本上,這不是良好的介面設計模式,更何況,AJAX的A意指Asynchronous(非同步),搞到網頁卡卡,恐有山寨AJAX之嫌。

是男人就要走非同步,是好漢就不該讓網頁卡住!

以下示範更友善的AJAX檢查寫法。首先,我們增設一個ajaxValidPass變數以標示是否通過檢核。按下btnSubmit時,先檢查ajaxValidPass是否已標為通過,若是則放行進行PostBack;若否,則將按鈕及輸入框設為disabled,並顯示"檢核中..."的訊息,再以$.get()或$.post()方式呼叫Server端檢查。在success函數中,先解除按鈕及輸入框的disabled,消除"檢核中..."字樣,並由傳回結果決定要顯示alert("Invalid!"),或是設定ajaxValidPass=true並自動呼叫$("#btnSubmit").click(),在這個程式觸發的click事件裡,由於ajaxValidPass已設為true,故會略過觸發AJAX檢核的邏輯,送出表單。

<%@ Page Language="C#" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request["m"] == "validate")
        {
            System.Threading.Thread.Sleep(5000);
            Response.Write(Request["t"] == "Darkthread" ? "OK" : "FAIL");
            Response.End();
        }
    }
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        Response.Write(txtCode.Text);
        Response.End();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Async Call</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.js" 
            type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            function validate() {
                $.get("Test.aspx",
                      { m: "validate", t: $("#txtCode").val(), r: Math.random() },
                      function (result) {
                          window.ajaxValidPass = (result == "OK");
                          //恢復按鈕及輸入框
                          $(".clsInput").removeAttr("disabled");
                          //清除檢核中...顯示字樣
                          $("#spnDisp").text("");
                          //檢核未過發出提示
                          if (!window.ajaxValidPass)
                              alert("Invalid!");
                          else //檢核若完成,自動按送出鈕送出表單
                              $("#btnSubmit").click();
                      });
            }
            $("#btnSubmit").click(function () {
                //若未經過AJAX檢核,觸發動作
                if (!window.ajaxValidPass) {
                    //停用按鈕及輸入框
                    $(".clsInput").attr("disabled", true);
                    //顯示檢核中
                    $("#spnDisp").text("檢核中...");
                    //開始AJAX檢核
                    validate();
                    //先傳回false,暫時不送出表單
                    return false;
                }
            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <img src="http://statics.plurk.com/a55bdb344892676b0fea545354654a49.gif" />
    <asp:TextBox ID="txtCode" runat="server" CssClass="clsInput"></asp:TextBox>
    <asp:Button ID="btnSubmit" runat="server" Text="Submit"  CssClass="clsInput"
        onclick="btnSubmit_Click" />
    <span id="spnDisp"></span>
    </form>
</body>
</html>

實際測試可驗證,等待期間香蕉人還是跳個不停、嗨到不行,網頁其他部分也反應如常,操作介面更加人性化囉!

後記: 以上程式碼以示範原理為主,故力求簡單、避免失焦。實務應用上,"檢核中..."字樣可用更精緻的圖文表示、停用按鈕與輸入框的可考慮改採BlockUI()。至於Client端的檢核,多半只能視為正式送出內容前的迅捷預覽,減少將無效資料傳至後端的無謂往返傳輸,千萬不可當成資料有效性的唯一檢核關卡,務必要在Server端重新檢核一次,以免因Script停用、故障或被破解等因素影響資料有效性,甚至危害資安。

【延伸閱讀】

邊做邊學 jQuery 系列 14- 呼叫總部-jQuery.ajax()


Comments

# by 阿尼

好!果然是鐵錚錚的好漢

# by jain

好用~~趕快記下來~~~

Post a comment