2012年5月24日

ASP.NET MVC Web API - 利用jQuery進行CRUD! (三) View篇

支援版本

  • ASP.NET MVC 4 Beta

終於進入到View篇了,本來是預計一天把三篇寫完的,結果沒想到變成一個禮拜一篇= =,現在終於知道連載的辛苦了,富木堅,對不起以前都一直罵你XDD;回到主題,上一篇我們介紹到Controller,也把整個Web Service完成了,但是我們還沒講到要怎樣利用JavaScript ( 利用jQuery這個函式庫 )對Web Service進行呼叫阿!?所以我們這篇來談談View的部分。

jQuery

小弟就不再這邊詳細的介紹jQuery了,我相信很多人應該都會使用,這邊只簡單的介紹一下jQuery是做甚麼的;jQuery 是一個非常方便,快速,程式碼又簡潔的JavaScript函式庫,原本我們用JavaScript來進行DOM物件的尋找、處理事件、動畫、瀏覽器版本還有Ajax等等,都會寫非常多的程式碼,所以就有位天才John Resig,寫了jQuery來大幅的簡化,總之,就是一個好物就是了;如果不會jQuery,可以參考一下jQuery的官網或是暗黑前輩的超完整教學,所以這部分小弟就不多介紹嚕。

Knockout

這裡必須還要再提一個東西,就是Knockout,他也是一個JavaScript的程式庫,不過不用擔心,小弟沒那麼慘忍XDD,一下要K Repository Patten,一下要看ASP.NET MVC Web API,一下又要看jQuery,所以這篇文章小弟不會用到Knockout;但為什麼要提到這個呢!?那是因為目前這個東西也正是被納入到ASP.NET MVC裡面,而且官方的範例中,就是大量地使用到這個程式庫,Knockout主要的用處是利用MVVM模式,來繫節畫面上的UI;詳細可以參考暗黑前輩的Knockout這篇文章,或是官網,或是Andy前輩的FAQ Book;不過在一次的重申,這篇文章不會用到Knockout,所以可以放鬆心情的去讀這幾位前輩的文章=v=。

View

前面鋪完路後,我們終於要正式開始撰寫View了,不知道大家還記不記得,第一章的時候,那個空白的圖?那時候我們建立起View後,並沒有在View裡面添加甚麼,現在我們終於要開始加上一些東西了;首先,我們可以先打開如下圖的檔案,這就是我們第一章就準備好的檔案。

image

我們首先先準備一下畫面,我們預期的畫面如下圖,基本上和官網的差不多 ( 官網範例沒有Delete喔XDD )。

image

HTML

接下來,HTML要怎樣寫哩,其實也沒甚麼特別的,就是利用了一些HTML5的標籤 ( 畢竟已經是HTML5的時代了 ),然後準備好一個Table,來顯示資料,並且準備一些輸入欄位,以便後續的CRUD( 這裡範例是用DIV標籤來包輸入欄位,當然也可以用DD、DT、或是li等標籤,看個人喜好吧 ),當然,我們也要準備一些 Button來觸發事件,所以我們準備了很多的Button ( 不是Submit按鈕喔!!兩個是有差異的。 ),來觸發各種事件,完成大致上如下。( 眼力好的人,可能已經會發現Button裡面已經有寫準備觸發的事件的Function名稱了,我們等下就會把這些Function建立起來了喔! )

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>

<body>
    <div id="body">
        <section class="content-wrapper main-content">
            <h3>Contacts</h3>
            <table>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Name</th>
                        <th>Phone</th>
                    </tr>
                </thead>
                <tbody id="customers">
                </tbody>
            </table>
        </section>
        <section id="detail" class="content-wrapper">
            <h3>View Contact</h3>
            <label for="contactId">ID</label>
            <input type="text" title="ID" id="contactId" size="5" />
            <input type="button" value="Get" onclick="find();" />
            <div>
                <label for="name">Name</label>
                <input type="text" title="Name" id="name" />
            </div>
            <div>
                <label for="phone"> Phone</label>
                <input type="text" title="Phone" id="phone" />
            </div>
            <div>
                <input type="button" value="Update" onclick="update();" />
                <input type="button" value="Delete" onclick="del();" />
                <input type="button" value="Add New" onclick="add();" />
            </div>
        <div>
            <p id="status"></p>
        </div>
        </section> 
    </div>
</body>
</html>

接下來,我們稍微弄一下美化吧,所以我們利用CSS進行美化,( 再次強調,不要用HTML做為美化的工具,美化的職責應該是由CSS負責的喔!! )。

CSS

好,不用擔心,我們沒有用到CSS3,下面是個很簡單的CSS,把CSS放到head標籤底下;CSS的內容也超簡單,基本上也只是把table、tr、th、td上個顏色,畢竟小弟現在不是要寫CSS的筆記,所以稍微設定一下而已;另外,小弟特別把從頭擷取index.cshtml的程式碼,我想這樣大家會比較了解CSS要放到哪邊。

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

<style type="text/css">
    table
    {
        border: 1px solid #000;
        border-collapse: collapse;
        color: #666666;
        min-width: 200px;
    }
    
    tr
    {
        border: 1px solid #000;
        line-height: 25px;
    }
    
    th
    {
        background-color: #B1C3CC;
        color: #000;
        font-size: 13px;
        text-align: left;
    }
    
    th, td
    {
        padding-left: 5px;
    }
</style>

</head>

完成後,就會變成這樣,有沒有有沒有,變漂亮了吧!!( 好吧,其實也沒漂亮到哪去…但我們這篇的重點是jQuery和Web Service,小細節就不要計較了XDD)。

image

JavaScript & jQuery

好,完成畫面後,就可以隨便亂點了,然後就會在偵錯工具出現這些錯誤( 記得偵錯工具要打開…),這當然很正常,因為我們JavaScript還沒開始寫嘛。

image

接下來我們開始寫JavaScript,以下是整個JavaScript,我們可以把整個JavaScript放到head標籤裡面,我們後面會針對細節做介紹。 ( 好的,我知道一些JavaScript放在head,效能等等之類的問題,但是,這不是我們這篇的重點=v= )

<script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"></script>
<script type="text/javascript">

    //清空status區段
    function clearStatus() {
        $('#status').html('');
    }

    var API_URL = "api/Customer/";

    //增加資料
    function add() {

        clearStatus();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL,
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: json,
            statusCode: {
                201 /*Created*/: function (data) {
                    getAll();
                }
            }
        });
    }

    //尋找資料
    function find() {

        clearStatus();

        var id = $('#contactId').val();
        if (id != "") {
            $.getJSON(API_URL + id,
            function (data) {
                $("#customers tr").remove();
                $("#customers").append("<tr>" +
                          "<td>" + data["Id"] + "</td>" +
                          "<td>" + data["Name"] + "</td>" +
                          "<td>" + data["Phone"] + "</td>" +
                          "</tr>");
            })
            .fail(
            function (xhr, textStatus, err) {
                $('#status').html('Error: ' + err);
            });
        } else {
            getAll();
        }
    }

    //更新資料
    function update() {
        clearStatus();

        var id = $('#contactId').val();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'PUT',
            contentType: 'application/json; charset=utf-8',
            data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //刪除指定資料
    function del() {
        clearStatus();

        var id = $('#contactId').val();

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'DELETE',
            contentType: 'application/json; charset=utf-8',
            //data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //取得所有資料
    function getAll() {
        clearStatus();

        //利用Get方式取得。
        $.getJSON(API_URL,
        function (data) {
            $("#customers tr").remove();
            for (i = 0; i < data.length; i++) {
                $("#customers").append("<tr>" +
                              "<td>" + data[i].Id + "</td>" +
                              "<td>" + data[i].Name + "</td>" +
                              "<td>" + data[i].Phone + "</td>" +
                              "</tr>");
            }
        })
    .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }

    //開始時,先把資料讀取進來
    $(document).ready(function () {
        getAll();
    });


</script>

接下來,我們針對每個細部,做一個講解,有講不好的地方,不要鞭我喔><。

清空

我們的第一個function,主要用途是清除status的資訊,在HTML裡面,有一段會顯示錯誤訊息等資訊,所以我們這邊也要準備一下,清除這些資訊的Function。

//清空status區段
    function clearStatus() {
        $('#status').html('');
    }

接下來繼續。

取得全部

我們先來介紹取得全部的這個Funciton,也就是getAll(),我們這邊利用jQuery的getJSON API進行處理,這個API預設會用HTTP的GET命令;當順利取得資料的時候,就會把HTML table裡面的東西移除掉,然後再利用迴圈的方式,把取得的JSON資料和HTML標籤td,一起塞到Table裡面去。

//取得所有資料
function getAll() {
    clearStatus();

    //利用Get方式取得。
    $.getJSON(API_URL,
    function (data) {
        $("#customers tr").remove();
        for (i = 0; i < data.length; i++) {
            $("#customers").append("<tr>" +
                          "<td>" + data[i].Id + "</td>" +
                          "<td>" + data[i].Name + "</td>" +
                          "<td>" + data[i].Phone + "</td>" +
                          "</tr>");
        }
    })
.fail(
    function (xhr, textStatus, err) {
        $('#status').html('Error: ' + err);
    });
}

然後我們來看看新增。

ADD

第二段我們要講的是ADD,但我們談論ADD之前,我們要先設一個變數,負責記錄網址位置,也就是API_URL;接下來,因為是新增,所以我們會需要準備傳遞資料,所以我們利用JSON.stringify來將我們填入表單的資料,轉成JSON格式。然後我們就要利用jQuery的ajax API來對Web Service進行呼叫;還記得嗎?HTTP的POST就是新增的意思,所以我們Type會設定POST,並且等傳回201時,執行getAll()這個Function。

var API_URL = "api/Customer/";

    //增加資料
    function add() {

        clearStatus();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL,
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: json,
            statusCode: {
                201 /*Created*/: function (data) {
                    getAll();
                }
            }
        });
    }

這樣ADD就完成了,下圖是執行結果,我們填入AA、AA的資料 ( Phone欄位沒有驗證,我真的知道><,是我的錯。 ),按下AddNew後,畫面會自動更新Table,出現第四筆資料;而下面的分析工具可以看到,真的送出了POST。

image

Find

接下來是尋找資料,其實尋找資料和getAll()很像,就不多加敘述了,但比較特別的是,我們會在網址 (API_URL)後面加上id,來尋找到想要找到的那一筆;如果沒有找到,就會在status區塊報錯。

//尋找資料
function find() {

    clearStatus();

    var id = $('#contactId').val();
    if (id != "") {
        $.getJSON(API_URL + id,
        function (data) {
            $("#customers tr").remove();
            $("#customers").append("<tr>" +
                      "<td>" + data["Id"] + "</td>" +
                      "<td>" + data["Name"] + "</td>" +
                      "<td>" + data["Phone"] + "</td>" +
                      "</tr>");
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    } else {
        getAll();
    }
}

執行結果如下,table會更新成尋到到的那筆,在下面的偵錯視窗可以看到,利用了Get。

image

Update

更新資料和新增資料很像,一樣是利用JSON.stringify來轉換成JSON格式,然後用PUT來進行傳送。

//更新資料
function update() {
    clearStatus();

    var id = $('#contactId').val();

    var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

    $.ajax({
        url: API_URL + id,
        cache: false,
        type: 'PUT',
        contentType: 'application/json; charset=utf-8',
        data: json,
        success: function () { getAll(); }
    })
    .fail(
    function (xhr, textStatus, err) {
        $('#status').html('Error: ' + err);
    });
}

執行結果如下,我們ID、Name、Phone都填好後,按下Update就會更新資料,並且取得最新的table,我們也可以看到下面的偵錯工具,可以發現到現在是用HTTP PUT命令。

image

Delete

刪除資料更簡單了,因為連JSON都不需要了,我們只要URL配合id,並且送出DELETE的指令就可以了!

//刪除指定資料
function del() {
    clearStatus();

    var id = $('#contactId').val();

    $.ajax({
        url: API_URL + id,
        cache: false,
        type: 'DELETE',
        contentType: 'application/json; charset=utf-8',
        //data: json,
        success: function () { getAll(); }
    })
    .fail(
    function (xhr, textStatus, err) {
        $('#status').html('Error: ' + err);
    });
}

測試一下,我們填入要刪除的ID,並按下Delete按鈕,table就會自動更新,我們也可以從偵錯視窗看到目前使用的是HTTP裡面的Delete命令。

image

畫面載入完成時

這是最後一小段的程式碼,小弟我希望畫面載入完成後,table會再動載入資料進來,所以加了這段。

//開始時,先把資料讀取進來
    $(document).ready(function () {
        getAll();
    });

到這邊就大功告成,我們最後再把整個index.cshtml看一遍吧。

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

<style type="text/css">
    table
    {
        border: 1px solid #000;
        border-collapse: collapse;
        color: #666666;
        min-width: 200px;
    }
    
    tr
    {
        border: 1px solid #000;
        line-height: 25px;
    }
    
    th
    {
        background-color: #B1C3CC;
        color: #000;
        font-size: 13px;
        text-align: left;
    }
    
    th, td
    {
        padding-left: 5px;
    }
</style>
<script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"></script>
<script type="text/javascript">

    //清空status區段
    function clearStatus() {
        $('#status').html('');
    }

    var API_URL = "api/Customer/";

    //增加資料
    function add() {

        clearStatus();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL,
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: json,
            statusCode: {
                201 /*Created*/: function (data) {
                    getAll();
                }
            }
        });
    }

    //尋找資料
    function find() {

        clearStatus();

        var id = $('#contactId').val();
        if (id != "") {
            $.getJSON(API_URL + id,
            function (data) {
                $("#customers tr").remove();
                $("#customers").append("<tr>" +
                          "<td>" + data["Id"] + "</td>" +
                          "<td>" + data["Name"] + "</td>" +
                          "<td>" + data["Phone"] + "</td>" +
                          "</tr>");
            })
            .fail(
            function (xhr, textStatus, err) {
                $('#status').html('Error: ' + err);
            });
        } else {
            getAll();
        }
    }

    //更新資料
    function update() {
        clearStatus();

        var id = $('#contactId').val();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'PUT',
            contentType: 'application/json; charset=utf-8',
            data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //刪除指定資料
    function del() {
        clearStatus();

        var id = $('#contactId').val();

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'DELETE',
            contentType: 'application/json; charset=utf-8',
            //data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //取得所有資料
    function getAll() {
        clearStatus();

        //利用Get方式取得。
        $.getJSON(API_URL,
        function (data) {
            $("#customers tr").remove();
            for (i = 0; i < data.length; i++) {
                $("#customers").append("<tr>" +
                              "<td>" + data[i].Id + "</td>" +
                              "<td>" + data[i].Name + "</td>" +
                              "<td>" + data[i].Phone + "</td>" +
                              "</tr>");
            }
        })
    .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }

    //開始時,先把資料讀取進來
    $(document).ready(function () {
        getAll();
    });


</script>
</head>
<body>
    <div id="body">
        <section class="content-wrapper main-content">
            <h3>Contacts</h3>
            <table>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Name</th>
                        <th>Phone</th>
                    </tr>
                </thead>
                <tbody id="customers">
                </tbody>
            </table>
        </section>
        <section id="detail" class="content-wrapper">
            <h3>View Contact</h3>
            <label for="contactId">ID</label>
            <input type="text" title="ID" id="contactId" size="5" />
            <input type="button" value="Get" onclick="find();" />
            <div>
                <label for="name">Name</label>
                <input type="text" title="Name" id="name" />
            </div>
            <div>
                <label for="phone"> Phone</label>
                <input type="text" title="Phone" id="phone" />
            </div>
            <div>
                <input type="button" value="Update" onclick="update();" />
                <input type="button" value="Delete" onclick="del();" />
                <input type="button" value="Add New" onclick="add();" />
            </div>
        <div>
            <p id="status"></p>
        </div>
        </section> 
    </div>
</body>
</html>

以上,終於寫完!

後記

寫完的當下,才發現KingKong前輩,和阿源哥哥前輩都有寫過類似的文章 ( 暈倒 ),但不管怎樣,這是小弟自己邊看邊寫的讀書筆記啦><,所以如果小弟沒寫好的地方,也可以去看看前輩們寫的超詳細文章喔!!

參考資料

4 則留言:

  1. Hi,
    我照著您的教學作一次後,執行時卻發生了 "javascript執行階段錯誤 '$' 未定義"的錯誤

    我把一開始先執行的方法註解掉, 想說試試看.
    //$(document).ready(function () {
    // getAll();
    // });

    不過因為每個動作一開始都會呼叫clearStatus();
    也都發生一樣的錯誤@@"

    而我在敲 var json = JSON.stringify... 這行的時候
    並沒有stringify 這個方法可以用XD

    諸多問題...因為我沒寫過javascript, 所以覺得學完這篇很多東西要學XDD

    不知道能不能請您給我個解決問題的方向, 謝謝!

    回覆刪除
  2. HI
    $未定義,應該就是jQuery沒載進來...
    如果對JS有興趣,又有閒時間XDD,可以考慮慢慢K犀牛書。
    另外,就如您所說的現在寫網頁其實要學很多東西,所以其實建議你,可以逐步突破,
    先從單一的部分慢慢看起,不然很容易卡關失去信心>"<....

    回覆刪除
  3. HI,
    謝謝您的指教>"<
    另外我想問的是, 這篇透過jQuery來做CRUD, 可以說是 透過jQuery呼叫Web API (REST API ?)來對資料庫做存取嗎?! 怎麼有種在Index.cshtml裡面就把事情做完了的感覺XD

    前面在Controller, Repository裡面實做的方法有用到嗎 ?
    突然覺得觀念混淆了QQ 再次謝謝您的指教~

    回覆刪除
  4. 這主要是為了分層,雖然範例上看起來透過jQuery來對db進行存取,但實務的架構上,我們還是會區分BLL來進行商業邏輯的撰寫,和DAL來進行資料庫的存取,而Repository就是在實作DAL~@@~.

    回覆刪除