迷你MVVM框架 avalonjs 學習教程十一、循環操做

avalon是經過ms-repeat實現對一組數據的批量輸出。這一組數據能夠是一個數組,也能夠是一個哈希(或叫對象)。咱們先從數組提及吧。javascript

第二節就說,凡是定義在VM中的數組,若是沒有以$開頭或者沒放在$skipArray數組裏,都會轉會監控數組。監控數組其實就是一個被重寫了push、unshift、shift、pop、 splice、sort、reverse方法的普通數組。固然它也添加了其餘一些方法,如set、 pushArray、remove、removeAt、removeAll、clear、ensure、 contains、size。咱們只要操做這些方法就能同步視圖。html

<!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是能夠配置的,如java

<!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遍實現的。git

有了循環綁定,咱們想作一個切換卡是很是簡單的。github

<!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;
                width: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循環代碼:oop

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類名顯示正確。ui

$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

循環綁定是一個很是重要的綁定,是屬於流程綁定之一,用法與注意點很是多,咱們能夠在這裏繼續學習。

相關文章
相關標籤/搜索