Fork me on GitHub

Knockout.js随手记(7)

数组元素的新增/移除事件

前两篇博客已经很清楚的知道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的常用功能。

                                如果你喜欢本文的话,推荐共勉,谢谢!

posted @   Halower  阅读(4180)  评论(7编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示