zoukankan      html  css  js  c++  java
  • 迷你MVVM框架 avalonjs 学习教程11、循环操作

    avalon是通过ms-repeat实现对一组数据的批量输出。这一组数据可以是一个数组,也可以是一个哈希(或叫对象)。我们先从数组说起吧。

    第二节就说,凡是定义在VM中的数组,如果没有以$开头或者没放在$skipArray数组里,都会转会监控数组。监控数组其实就是一个被重写了push、unshift、shift、pop、 splice、sort、reverse方法的普通数组。当然它也添加了其他一些方法,如set、 pushArray、remove、removeAt、removeAll、clear、ensure、 contains、size。我们只要操作这些方法就能同步视图。

    <!DOCTYPE html>
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js" ></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    array: ["aaa","bbb","ccc"]
                })
    
            </script>
        </head>
        <body ms-controller="test">
            <ul>
                <li ms-repeat="array">{{el}} --- {{$index}}</li>
            </ul>
        </body>
    </html>
    

    enter image description here enter image description here 上面就是array被改造成监控数组后的样式,添加了大量属性与方法。 ms-repeat是配合与监控数组使用的。我们注意到在ms-repeat的作用范围下,多出了el、$index两个变量,而它们在VM(ViewModel)中是寻不到它们的踪影。这是循环绑定特有的功能,其中el称之为代理VM,$index是与这个el相对应的索引值。并且这个el是可以配置的,如

    <!DOCTYPE html>
        <html>
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                <script src="avalon.js" ></script>
                <script>
                    var model = avalon.define({
                        $id: "test",
                        array: ["aaa","bbb","ccc"]
                    })
                </script>
            </head>
            <body ms-controller="test">
                <ul>
                    <li ms-repeat-item="array">{{item}} --- {{$index}}</li>
                </ul>
            </body>
        </html>
    

    说起作用域,我们可以看到ms-repeat是将当前元素根据当前数组的个数,以原元素为模板,在原地重复复制N遍实现的。

    有了循环绑定,我们想做一个切换卡是非常简单的。

    <!DOCTYPE html>
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js" ></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    array: ["aaa", "bbb", "ccc"],
                    currentIndex: 0,
                    changeIndex: function(i) {
                        model.currentIndex = i
                    }
                })
            </script>
            <style>
                .ms-tabs{
                    border: 1px solid black;
                    padding: 1em;
                    300px;
                    height:100px;
                }
                .ms-tigger{
                    background:#DDD;
                    margin-right:1em;
                }
                .ms-active{
                    background:#CD235C;
                }
            </style>
        </head>
        <body ms-controller="test">
            <button type="button"
                    class="ms-tigger"
                    ms-repeat="array" 
                    ms-class="ms-active: currentIndex === $index" 
                    ms-click="changeIndex($index)">切换键{{$index+1}}</button>
            <div class="ms-tabs"
                 ms-repeat="array"
                 ms-if-loop="currentIndex == $index">{{el}}</div>
        </body>
    </html>
    

    enter image description here 这里有一个ms-if-loop,第三节就介绍过绑定属性的执行顺序,ms-if是先于ms-repeat执行的,当我想在循环时,要根据元素的情况做一些分支判定时就实现不了。因此需要一个晚于ms-repeat的ms-if,于是ms-if-loop就应运而生了。

    在循环过程中,ms-repeat除了会产生el、$index等临时变量,还有其他变量供我们调遣。

    • $index,这个一个数字,为元素对应的索引值
    • $first,这是一个布尔,判定它是否第一个
    • $last,这是一个布尔,判定它是否最后一个
    • $remove,这是一个方法,移除此数组元素
    • $outer,这是一个对象,用于获取外围循环中的VM对象,它里面包含$index, $first, $last, $remove等属性。

    它们的关系就如下面的javascript循环代码:

    for(var i = 0, n = array.length; i < n; i++){  //----> ms-each-el=array
       var el = array[i] // $index --> i
       for(var j = 0, k = el.length; j < k; j++){  //---> ms-each-elem=el
            var elem = el[j] // elem.$outer --->  el
        }
    }
    

    下面我们看一下$first、 $last、$remove的使用方法:

    <!DOCTYPE html>
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js" ></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    array: ["aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg"]
                })
            </script>
            <style>
                .last{
                    background: purple;
                }
                .first{
                    background:violet;
                }
            </style>
        </head>
        <body ms-controller="test">
            <ul>
                <li ms-repeat-xx="array" 
                    ms-class="last: $last" 
                    ms-class-1="first: $first" 
                    ms-click="$remove">{{xx}}:{{$index}}</li>
            </ul>
        </body>
    </html>
    

    enter image description here 当我们点击LI元素时,它就会自动从监控数组移除对应的元素,并立即同步视图,删除我们刚才点击的元素节点,同时会调整其他元素的$index、$first、$last,从而确保first、 last类名显示正确。

    $outer主要是用在二维数组或多维数组里。

    <!DOCTYPE html>
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js" ></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    array: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
                })
            </script>
        </head>
        <body ms-controller="test">
            <table border="1">
                <tr ms-repeat-el="array">
                    <td ms-repeat-elem="el">{{elem}}  它位于第<b style="color:orchid">{{$outer.$index}}</b>行</td>
                </tr>
            </table>
        </body>
    </html>
    

    enter image description here

    <!DOCTYPE html>
    <html>
        <head>
            <title>avalon入门</title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js" type="text/javascript"></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    num: [1,2,3],
                    data: ["a", "b", "c"]
                });
    
            </script> 
        </head>
        <body>
            <div ms-controller="test">
                <div ms-repeat="num">
                    <strong ms-repeat="data">
                        {{el}}: {{$outer.el}}
                    </strong>
                </div>
            </div>
    
        </body>
    </html>

    如果我们想在绑定属性得到当前数组的长度,请记得使用size方法,不要直接用length属性啊。我们来一个复制,演示怎么调用它的方法来同步视图的。

    <!DOCTYPE HTML>
    <html>
        <head>
            <title>ms-repeat</title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js" ></script>
            <script>
                avalon.define("test", function(vm) {//这里是使用avalon.define的旧风格
                    vm.array = ["1", "2", "3", "4"]
                    "push,unshift,remove,ensure".replace(/w+/g, function(method) {
                        vm[method] = function(e) {
                            if (this.value && e.which == 13) {//this为input元素
                                vm.array[method](this.value);
                                this.value = "";
                            }
                        }
                    })
    
                    vm.removeAt = function(e) {
                        if (isFinite(this.value) && e.which == 13) {//this为input元素
                            var a = ~~this.value
                            vm.array.removeAt(a)
                            this.value = "";
                        }
                    }
                    "pop,shift,sort,reverse".replace(/w+/g, function(method) {
                        vm[method] = function(e) {
                            vm.array[method]();
                        }
                    })
                });
    
            </script>
        </head>
        <body ms-controller="test">
            <p>监控数组拥有以下方法,我们可以操作它们就能同步对应的区域</p>
            <blockquote>
                push, pushAll, shift, unshift, pop, slice, splice, remove, removeAt, removeAll, clear, ensure, sort, reverse, set
            </blockquote>
            <ul>
                <li  ms-repeat="array">数组的第{{$index+1}}个元素为{{el}}</li>
            </ul>
            <p>对数组进行push操作,并回车<input ms-keypress="push"></p>
            <p>对数组进行unshift操作,并回车<input ms-keypress="unshift"></p>
            <p>对数组进行ensure操作,并回车<input ms-keypress="ensure"><br/>
                (只有数组不存在此元素才push进去)</p>
            <p>对数组进行remove操作,并回车<input ms-keypress="remove"></p>
            <p>对数组进行removeAt操作,并回车<input ms-keypress="removeAt"></p>
            <p><button type="button" ms-click="sort">对数组进行sort操作</button></p>
            <p><button type="button" ms-click="reverse">对数组进行reverse操作</button></p>
            <p><button type="button" ms-click="shift">对数组进行shift操作</button></p>
            <p><button type="button" ms-click="pop">对数组进行pop操作</button></p>
            <p>当前数组的长度为<span style="color:red">{{array.size()}}</span>,注意 我们无法修改数组length的固有行为,因此它无法同步视图,需要用size方法。</p>
    
        </body>
    </html>
    

    enter image description here 通过操作属性就能操作视图是不是很爽呢!要知道上面的代码如果换成jQuery来不写不知要写多少行!

    我们再来一点实用的例子。

    <!DOCTYPE html>
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width">
            <script src="avalon.js"></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    data: [{checked: false}, {checked: false}, {checked: false}],
                    allchecked: false,
                    checkAll: function() {
                        var bool = model.allchecked = this.checked
                        model.data.forEach(function(el) {
                            el.checked = bool
                        })
                    },
                    checkOne: function() {
                        if (!this.checked) {
                            model.allchecked = false
                        } else {//avalon已经为数组添加了ecma262v5的一些新方法
                            model.allchecked = model.data.every(function(el) {
                                return el.checked
                            })
                        }
                    }
                })
            </script>
        </head>
        <body>
            <table ms-controller="test" border="1">
                <tr>
                    <td><input type="checkbox" ms-duplex-radio="allchecked" data-duplex-changed="checkAll"/>全选</td>
                </tr>
                <tr ms-repeat="data">
                    <td><input type="checkbox"  ms-duplex-radio="el.checked" ms-data-index=$index data-duplex-changed="checkOne"/>xxxxxxxxxxxx</td>
                </tr>
            </table>
        </body>
    </html>
    

    enter image description here

    此外,我们还可以通过data-each-rendered来指定这些元素都插入DOM被渲染了后执行的回调,this指向元素节点,有一个参数表示为当前的操作,是add、del、 move、 index还是clear。

    上面我们说了这么有关数组的东西,我们再来看它是如何操作哈希的。对于哈希,ms-repeat内部只会产生$key、 $val、 $outer三个变量,不存在$index什么的。$key就是属性名,$val就是属性值,$outer与之前的讲解相同。如果你想在对象循环时使用$index,可以这样做:

    <!DOCTYPE html>
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width">
            <script src="avalon.js"></script>
            <script>
                var index = 0
                var model = avalon.define({
                    $id: "test",
                    data:{
                        aaa: 1111,
                        bbb: 2222,
                        ccc: 3333,
                        ddd: 4444
                    },
                    getIndex: function(){
                        return index++
                    }
                })
            </script>
        </head>
        <body ms-controller="test">
            <ul>
                <li ms-repeat="data" >{{getIndex()}}、{{$key}}--{{$val}}</li>
            </ul>
        </body>
    </html>
    

    enter image description here

    如果我们想控制对象属性的输出顺序,或让某些元素不输出来,那么我们可以使用data-with-sorted回调。它用ms-repeat、ms-with绑定,赶对象渲染之前触发,要求输出一个字符串数组,对象的键值对会根据它依次输出;框架默认会输入原对象的所有键名构成的数组作为参数。

    我们改一下上面的例子:

    <!DOCTYPE html>
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width">
            <script src="avalon.js"></script>
            <script>
                var index = 0
                var model = avalon.define({
                    $id: "test",
                    data:{
                        aaa: 1111,
                        bbb: 2222,
                        ccc: 3333,
                        ddd: 4444
                    },
                    keys: function(a){
                        console.log(a)
                        console.log(this)
                        return  ["ccc","ddd","aaa"]
                    },
                    getIndex: function(){
                        return index++
                    }
                })
            </script>
        </head>
        <body ms-controller="test">
            <ul>
                <li ms-repeat="data" data-with-sorted="keys" >{{getIndex()}}、{{$key}}--{{$val}}</li>
            </ul>
        </body>
    </html>
    

    enter image description here

    不过ms-repeat只能循环自身,如果有时我们碰到一些复杂的结构,如定义列表,那么我们可以使用ms-each、 ms-with。ms-each是用于循环数组,ms-with是循环对象。除了循环范围不一样外,其他与ms-repeat没什么不同。

    <!DOCTYPE html>
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width">
            <script src="avalon.js"></script>
            <script>
                var model = avalon.define({
                    $id: "test",
                    data: [
                        {text: "title1", value: 111111},
                        {text: "title2", value: 222222},
                        {text: "title3", value: 333333}
                    ]
                })
            </script>
        </head>
        <body ms-controller="test">
            <dl ms-each="data">
                <dt>{{el.text}}</dt>
                <dd>{{el.value}}</dd>
            </dl>
        </body>
    </html>
    

    enter image description here

    循环绑定是一个非常重要的绑定,是属于流程绑定之一,用法与注意点非常多,我们可以在这里继续学习。

  • 相关阅读:
    2014.12.31今年最后的一天
    leetcode11 盛最多水的容器(Medium)
    leetcode23 合并k个排序链表(Hard)
    leetcode148 排序链表(Medium)
    leetcode48 旋转图像(Medium)
    leetcode227基本计算器II (Medium)
    leetcode338 比特位计数(Medium)
    leetcode32 最长有效括号(Hard)
    leetcode面试题10.01 合并排序的数组(Easy)
    leetcode55 跳跃游戏(Medium)
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/3992451.html
Copyright © 2011-2022 走看看