avalon2學習教程09循環操作


avalon2的循環指令的用法完全改變了。avalon最早期從knockout那樣抄來ms-each,ms-with,分別用於數組循環與對象循環。它們都是針對元素內容進行循環。后來又從angular那里抄來了ms-repeat, 這是循環元素內部的。

到avalon2,這三個指令合並成一個ms-for指令 ,用法與angular更相似,但沒有$index, $last, $first, $middle.

如果想得到數組元素或對象的鍵值,在in關鍵字前指定一個新變量

<div ms-for="el in @arrayOrObject">{{el}}</div>

如果要指定數組索引值或對象的鍵名,那么需要加一個小括號

<div ms-for="(index,el) in @arrayOrObject">{{el}}</div>

我們可以用limitBy, filterBy, orderBy, selectBy過濾器生成新的循環體

<div ms-for="(index,el) in @arrayOrObject | filterBy('name')">{{el}}</div>

如果用limitBy過濾器,那么數組的長度或對象的大小會變小,那我們現在就不
知道現在的長度,因此我們需要另一個變量引用新對象新數組

<div ms-for="(index,el) in @arrayOrObject  as newArray| filterBy('name')">{{el}}::{{newArray.length}}</div>

如果想實現之前的$fist, $last效果,那就需要用到js指令

<div ms-for="(index,el) in @arrayOrObject  as newArray| filterBy('name')">
<!--ms-js:var $first = $index === 0 -->
<!--ms-js:var $last = $index === new Array -2 -->
</div>

這是我們第一次見到以注釋節點存在的指令了。實質上,ms-if的值為false,創建的注釋節點也算是一種注釋指令。

而avalon2是沒有像angular那樣的ng-repeat-start, ng-repeat-end這樣圈 定某個范圍的輔助指令。換言之,不能像ms-repeat那樣循環多個元素。

這時我們需要了解一下其內部機制。這個以元素屬性存在的ms-for指令,會翻譯成以注釋節點存在的ms-for指令。

<div class='panel' ms-for="($index, el) in @array">{{el}}::{{$index}}</div>

等價於

<!--ms-for:($index,el) in @array-->
<div class='panel'>{{el}}::{{$index}}</div>
<!--ms-for-end:-->

這有點復雜,但可以解決我們循環多個元素的問題

<!--ms-for:($index,el) in @array-->
<td>{{el.td1}}</td>
<td>{{el.td2}}</td>
<!--ms-for-end:-->

注意,avalon2的監控數組已經移除size()方法,由於內部使用了虛擬DOM,你直接使用@array.length就能得知道當前長度了。

avalon2也沒有angular的track by機制,或像React那樣強制使用key.這種為優化排序性能的方法,avalon內部幫你搞定,就不需要你多寫什么了。

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="./dist/avalon.js" ></script>
        <script>
            var vm = avalon.define({
                $id: "test",
                array: ["aaa","bbb","ccc"]
            })
            setTimeout(function(){
                 vm.array = ['ccc','dd1','dd2','dd3']
            },3000)
           

        </script>
    </head>
    <body ms-controller="test">
        <ul>
            <li ms-for="($index, el) in @array">{{el}} --- {{$index}}</li>
        </ul>
    </body>
</html>

圖片描述

我們再來看一下如何循環二維數組

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="./dist/avalon.js" ></script>
        <script>
            var vm = avalon.define({
                $id: "test",
                array: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
            })
            setTimeout(function(){
                vm.array.set(0, [13,14,15,16])
            },3000)
        </script>
    </head>
    <body ms-controller="test">
        <table border="1">
            <tr ms-for="($index, el) in @array">
                <td ms-for="elem in el">{{elem}}  它位於第<b style="color:orchid">{{$index}}</b>行</td>
            </tr>
        </table>
    </body>
</html>

圖片描述
圖上的這些注釋節點,你用過angular也會發現類似的東西,這是方便框架對這些元素進行排序增刪操作設計的。大家不要手動移除它們。

我們再看一個經典的例子,如何通過操作數組為列表添加或移除某一項!另,大家也可以對照這里看看avalon1是怎么實現的,你就明白avalon2在這方面的巨大優勢與便利。


<!DOCTYPE HTML>
<html>
    <head>
        <title>ms-repeat</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="./dist/avalon.js" ></script>
        <script>

            var definition = {
                $id: 'test',
                array: ['1', '2', '3', '4'],
                removeAt: function (e) {
                    var elem = e.target
                    if (isFinite(elem.value)) {//this為input元素
                        var a = ~~elem.value
                        this.array.removeAt(a)
                        elem.value = ''
                    }
                }
            }
            'push,unshift,remove,ensure'.replace(avalon.rword, function (method) {
                definition[method] = function (e) {
                    //avalon2中,所有通過ms-on-* 及其變體綁定的事件,其this都是指向vm,
                    //而元素本身則通過e.targeta得到
                    var elem = e.target
                    if (elem.value) {
                        this.array[method](elem.value)
                        elem.value = ''
                    }
                }
            })
            'pop,shift,sort,reverse'.replace(avalon.rword, function (method) {
                definition[method] = function (e) {
                    this.array[method]()
                }
            })
            
            avalon.define(definition)

        </script>
    </head>
    <body ms-controller="test">
        <p>監控數組擁有以下方法,我們可以操作它們就能同步對應的區域</p>
        <blockquote>
            push, shift, unshift, pop, slice, splice, remove, removeAt, removeAll, clear,
            ensure, pushArray, sort, reverse, set
        </blockquote>
        <ul>
            <li ms-for="($index,el) in @array">數組的第{{$index+1}}個元素為{{el}}</li>
        </ul>
        <p>對數組進行push操作,並回車<input ms-keypress="@push | enter"></p>
        <p>對數組進行unshift操作,並回車<input ms-keypress="@unshift | enter"></p>
        <p>對數組進行ensure操作,並回車<input ms-keypress="@ensure | enter"><br/>
            (只有數組不存在此元素才push進去)</p>
        <p>對數組進行remove操作,並回車<input ms-keypress="@remove | enter"></p>
        <p>對數組進行removeAt操作,並回車<input ms-keypress="@removeAt | enter"></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.length}}</span>。</p>

    </body>
</html>

圖片描述
最后我們來一個表格的實用例子。之前avalon的大表格渲染時存在性能問題,現在大大得到改進了。

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="./dist/avalon.js"></script>
        <script>
            var vm = avalon.define({
                $id: 'for3',
                header: ['name','age','sex'],
                list: []
            })
            var sexMap = {
                true: "男",
                false: "女"
            }
            function genData(n){
                var ret = []
                for(var i =0 ; i< n; i++){
                    ret.push({
                        name: Math.random(), 
                        age: 3+ Math.ceil((Math.random() *30)),
                        sex: sexMap[1-Math.random() > 0.5],
                        desc: Math.random()
                    })
                }
                return ret
            }
           setInterval(function(){
                var t1 = Date.now();
                vm.list = genData(100)
                console.log('total ' + (Date.now() - t1) + ' ms');
            }, 70);
        </script>
    </head>
    <body>

        <div ms-controller='for3' >
            <table border="1">
                <tr><th ms-for='el in @header'>{{el}}</th></tr>
               <tr ms-for='tr in @list'>
                    <td ms-for='td in tr | selectBy(["name","age","sex"])' ms-attr="{align:td === 'age' ?'left':'right'}">{{td}}</td>
                </tr>
            </table>
        </div>
    </body>
</html>

圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM