Knockout.js隨手記(7)

Halower發表於2013-07-26

陣列元素的新增/移除事件

前兩篇部落格已經很清楚的知道knockout.js通過observableArray()陣列元素增減,可以實時的反映在UI上。當然我們想在陣列增加或移除元素時加上自定義邏輯就好比一個觸發器的感覺,可以嗎?

foreach提供了afterAdd及beforeRemove兩個額外的事件,允許在陣列新增、移除元素時執行特定邏輯。在此繼續沿用先前的使用者列表呈現範例,加上兩個效果:

  • 新增資料時,將最新加入的資料和表格進行著色修飾
  • 刪除資料時,加上資料淡出特效

而在ViewModel裡我們加上兩個函式:

   //新增物件後才觸發,第一次forach並不會觸發
            self.afterAddEvent = function (element, index, data) {
                //通過nodeType過濾,只處理Element Node
                if (element.nodeType==1)  
                {
                    $(".new").removeClass("new");
                    $(element).addClass("new");
                }
            };
            //注意: beforeRemove事件後,要自已移除被刪除元素
            self.beforeRemoveEvent = function (element, index, data) {
                if (element.nodeType == 1) {
                    $(element)
                    .css("background-color", "#ff6a00")
                    .animate({ opacity: 0.2 },1000, function () {
                        $(this).remove();
                    })

                }
            };

afterAdd及beforeRemove函式會固定收到三個引數,element、index及data,其中element為模板容器中的各元素,即:

          <tr>
                    <td><span data-bind="text: id"></span></td>
                    <td><span data-bind="text: name"></span></td>
                    <td><span data-bind="text: score"></span></td>
                    <td><a href='#' data-bind="click: $root.removeUser">Remove</a></td>
                </tr>

實際運作時afterAdd/beforeRemove會收到不同的element被呼叫三次,原因是除了<tr>之外,<tbody>到<tr>之間的空白、</tr>到</tbody>間的空白也各算一個Element,(FF和chrome是忽略這個空格的)其nodeType為3即TEXT_NODE,代表TEXT_NODE。因此三次傳入的element分別為TEXT_NODE、ELEMENT_NODE、TEXT_NODE,而第二次傳入的ELEMENT_NODE是<tr>...</tr>間的內容,才是我們需要處理的物件,故加入if (elems.nodeType == 1)的判斷。

要注意,一旦呼叫了了beforeRemove,konckout.js就不再自動幫你移除該筆資料在網頁對應的元素,必須自行處理,但這也提供開發人員絕對的控制權,可自由安排HTML元素要怎麼從網頁上退出。

完整程式碼如下:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index2</title>
    <style>
        table { width: 385px }
        td,th { border: 1px solid #0094ff; text-align: center; }
        .new { color: #0094ff; background-color:#b6ff00}       
    </style>

    <script src="~/Scripts/jquery-2.0.3.js"></script>
    <script src="~/Scripts/knockout-2.3.0.js"></script>
    <script  type="text/javascript">
        //定義user資料物件
        function UserViewModel(id,name,score) {
            var self = this;
            self.id = id;
            self.name =ko.observable(name);
            self.score =ko.observable(score);
        }
        //定義ViewModel
        function ViewModel() {
            var self = this;
            self.users = ko.observableArray();//新增動態監視陣列物件
            self.removeUser = function (user) {
                self.users.remove(user);
            }
            self.totalscore = ko.computed(function () {
                var total = 0;
                $.each(self.users(), function (i, u) {
                    total += u.score();
                })
                return total;
            });
            //新增物件後才觸發,第一次forach並不會觸發
            self.afterAddEvent = function (element, index, data) {
                //通過nodeType過濾,只處理Element Node
                if (element.nodeType==1)  
                {
                    $(".new").removeClass("new");
                    $(element).addClass("new");
                }
            };
            //注意: beforeRemove事件後,要自已移除被刪除元素
            self.beforeRemoveEvent = function (element, index, data) {
                if (element.nodeType == 1) {
                    $(element)
                    .css("background-color", "#ff6a00")
                    .animate({ opacity: 0.2 },600, function () {
                        $(this).remove();
                    })

                }
            };
            };
        $(function () {
            var vm = new ViewModel();
            //預先新增一些資料
            vm.users.push(new UserViewModel("d1", "rohelm", 121));
            vm.users.push(new UserViewModel("d2", "halower", 125));
            $("#btnAddUser").click(function () { 
                vm.users.push(new UserViewModel(
                    $("#u_id").val(),
                    $("#u_name").val(),
                   parseInt($("#u_score").val())));
            });
            $("#btnUpdateScore").click(function () {
                vm.users()[vm.users().length-1].score(125).name("HelloWorld!");
            });
            ko.applyBindings(vm);
        });
    </script>
</head>
<body>
     <section style="margin:250px">
         <section>
         ID<input type="text" id="u_id" style="width:30px">
         Name<input type="text" id="u_name" style="width:30px">
         Score<input type="text" id="u_score" style="width:30px"><br/>
         <input  value="Add" id="btnAddUser" type="button" style="width:200px; background-color:#ff6a00;"/><br/><span data-bind="text: users().length"></span> 條--------------合計 <span data-bind="text: totalscore"></span></section>
       <section>
           <table>
            <thead>
                <tr><th>ID</th><th>Name</th><th>Score </th><th>Option</th></tr>
            </thead>
            <tbody  data-bind="foreach: { data: users, afterAdd: afterAddEvent, beforeRemove: beforeRemoveEvent}">
                <tr>
                    <td><span data-bind="text: id"></span></td>
                    <td><span data-bind="text: name"></span></td>
                    <td><span data-bind="text: score"></span></td>
                    <td><a href='#' data-bind="click: $root.removeUser">Remove</a></td>
                </tr>
            </tbody>
        </table>
            <input  value="Update測試" id="btnUpdateScore" type="button" style="width:200px; background-color:#ff6a00;"/><br/>
       </section>
     </section>
</body>
</html>

執行效果如下:

備註:

    本文版權歸大家共用,不歸本人所有,所有知識都來自於官網支援,書本,國內外論壇,大牛分享等等......後續將學習knockout.js的常用功能。

                                如果你喜歡本文的話,推薦共勉,謝謝!

相關文章