avalon2學習教程11數據聯動


在許多表單應用,我們經常遇到點擊一個復選框(或下拉框)會引發旁邊的復選框(或下拉框)發生改變,這種聯動效果用avalon來做是非常簡單的。因為avalon擁有經典MVVM框架的一大利器,雙向綁定!絕大部分的指令是從vm單向拍到頁面,而雙向綁定,則通過監聽元素的value值變化,反向同步到vm中。如果沒有這種機制,則需要引入額外的機制(flux雲雲)來處理此事。

avalon中,雙向綁定是由雙工指設,ms-duplex實現的。這個指令在1.0中已經不斷增強,到2.0,它的服務對象已經不局限於表單元素,還擴展到可編輯元素(contenteditable=true)上了。

此外ms-duplex還可以與新加入的ms-validate指令一起使用。因此雙工指令是集成數據轉換,數據格式化,數據驗證,光標處理4大功能。

數據轉換與之前1.5一樣,使用四大轉換器

ms-duplex-string="@aaa"
ms-duplex-number="@aaa"
ms-duplex-boolean="@aaa"
ms-duplex-checked="@aaa"

前三個是將元素的value值轉換成string, number, boolean(只有為'false'時轉換為false),最后是根據當前元素(它只能是radio或checkbox)的checked屬性值轉換為vm對應屬性的值。

它們都是放在屬性名上。當數據從元素節點往vmodel同步時,轉換成預期的數據。

數據格式化是放在屬性值時,以過濾器形式存在,如

ms-duplex='@aaa | uppercase'
ms-duplex='@aaa | date('YYYY:MM:dd')'

此外還存在兩個控制同步時機的過濾器,change與debounce。

change過濾器相當於之前的data-duplex-event="change".
debounce是對頻繁輸入進行節流處理。它既不像那oninput事件那樣密集(由於使用了虛擬DOM,每一個字符,都會重新短成一個全新的虛擬DOM樹),也不像onchange事件那么滯后。這在自動元素的suggest組件中非常有用。debounce可以傳參,為毫秒數

ms-duplex='@aaa | debounce(300)'

然后是數據驗證,這必須在所有表單元素的上方,加上ms-validate才會生效。這時每個表單元素要加上data-duplex-validator.

<form ms-validate="@validation">
<input ms-duplex='@aaa' 
       data-validators='require,email,maxlength' 
       data-maxlength='4' 
       data-maxlength-message='太長了' >
</form>

最后是光標處理,目的是確保光標不會一下子跑到最前還是最后。

除此之后,ms-duplex還有一個回調,data-duplex-changed,用於與事件綁定一樣,默認第一個參數為事件對象。如果傳入多個參數,那么使用$event為事件對象占位。

現在我們來一些實際的例子!

全選與非全選

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script src="./dist/avalon.js"></script>
        <script>
            var vm = avalon.define({
                $id: "test",
                data: [{checked: false}, {checked: false}, {checked: false}],
                allchecked: false,
                checkAll: function (e) {
                    var checked = e.target.checked
                    vm.data.forEach(function (el) {
                        el.checked = checked
                    })
                },
                checkOne: function (e) {
                    var checked = e.target.checked
                    if (checked === false) {
                        vm.allchecked = false
                    } else {//avalon已經為數組添加了ecma262v5的一些新方法
                        vm.allchecked = vm.data.every(function (el) {
                            return el.checked
                        })
                    }
                }
            })
        </script>
    </head>
    <body>
        <table ms-controller="test" border="1">
            <tr>
                <td><input type="checkbox" 
                           ms-duplex-checked="@allchecked" 
                           data-duplex-changed="@checkAll"/>全選</td>
            </tr>
            <tr ms-for="($index, el) in @data">
                <td><input type="checkbox" ms-duplex-checked="el.checked" data-duplex-changed="@checkOne" />{{$index}}::{{el.checked}}</td>
            </tr>
        </table>

    </body>
</html>

圖片描述

我們仔細分析其源碼,allchecked是用來控制最上面的復選框的打勾情況,數組中的checked是用來控制下面每個復選框的下勾情況。由於是使用ms-duplex,因此會監聽用戶行為,當復選框的狀態發生改變時,就會觸發data-duplex-changed回調,將當前值傳給回調。但這里我們不需要用它的value值,只用它的checked值。

最上面的復選框對應的回調是checkAll,它是用來更新數組的每個元素的checked屬性,因此一個forEach循環賦值就是。

下面的復選框對應的checkOne,它們是用來同步最上面的復選框,只要它們有一個為false上面的復選框就不能打勾,當它們被打勾了,它們就得循環整個數組,檢查是否所有元素都為true,是才給上面的checkall屬性置為true。

現在 我們學了循環指令,結合它來做一個表格看看。現在有了強大無比的orderBy, limitBy, filterBy, selectBy。我們做高性能的大表格是得心應手的!

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 
        <script src="./dist/avalon.js" ></script>
        <script>
            if (!Date.now) {//fix 舊式IE
                Date.now = function() {
                    return new Date - 0;
                }
            }
            var model = avalon.define({
                $id: "test",
                selected: "name",
                options: ["name", "size", "date"],
                trend: 1,
                data: [
                    {name: "aaa", size: 213, date: Date.now() + 20},
                    {name: "bbb", size: 4576, date:Date.now() - 4},
                    {name: "ccc", size: 563, date: Date.now() - 7},
                    {name: "eee", size: 3713, date: Date.now() + 9},
                    {name: "555", size: 389, date: Date.now() - 20}
                ]
            })

        </script>
    </head>
    <body ms-controller="test">
        <div style="color:red">
            <p>本例子用於顯示如何做一個簡單的表格排序</p>
        </div>
        <p>
            <select ms-duplex="@selected">
                <option  ms-for="el in @options">{{el}}</option>
            </select>
            <select ms-duplex-number="@trend">
                <option value="1">up</option>
                <option value="-1">down</option>
            </select>
        </p>
        <table width="500px" border="1">
            <tbody >
                <tr ms-for="el in @data | orderBy(@selected, @trend)">
                    <td>{{el.name}}</td> <td>{{el.size}}</td> <td>{{el.date}}</td>
                </tr>
            </tbody>
        </table>
    </body>
</html>

圖片描述

我們再來一個文本域與下拉框的聯動例子,它只用到ms-duplex,不過兩個控件都是綁定同一個屬性。

<!DOCTYPE html>
<html>
    <head>
        <script src="./dist/avalon.js"></script>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script>
            avalon.define({
                $id: "fruit",
                options: ["蘋果", "香蕉", "桃子", "雪梨", "葡萄", "哈蜜瓜", "橙子", "火龍果", "荔技", "黃皮"],
                selected: "桃子"
            })
        </script>
    </head>
    <body ms-controller="fruit">
        <h3>文本域與下拉框的聯動</h3>
        <input  ms-duplex="@selected" />
        <select ms-duplex="@selected" >
            <option ms-for="el in @options" ms-attr="{value: el}" >{{el}}</option>
        </select>
    </body>
</html>

圖片描述

<!DOCTYPE html>
<html>
    <head>
        <script src="./dist/avalon.js"></script>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script>
            var map = {
                "中國": ["江南四大才子", "初唐四傑", "戰國四君子"],
                "日本": ["日本武將", "日本城堡", "幕府時代"],
                "歐美": ["三大騎士團", "三大魔幻小說", "七大奇跡"],
                "江南四大才子": ["祝枝山", "文征明", "唐伯虎", "周文賓"],
                "初唐四傑": ["王勃", "楊炯", "盧照鄰", "駱賓王"],
                "戰國四君子": ["楚國春申君黃歇", "齊國孟嘗君田文", "趙國平原君趙勝", "魏國信陵君魏無忌"],
                "日本武將": ["織田信長", "德川家康", "豐臣秀吉"],
                "日本城堡": ["安土城", "熊本城", "大坂城", "姬路城"],
                "幕府時代": ["鐮倉", "室町", "豐臣", "江戶"],
                "三大騎士團": ["聖殿騎士團", "醫院騎士團", "條頓騎士團"],
                "三大魔幻小說": ["冰與火之歌", "時光之輪", "荊刺與白骨之王國"],
                "七大奇跡": ["埃及胡夫金字塔", "奧林匹亞宙斯巨像", "阿爾忒彌斯月神殿", "摩索拉斯陵墓", "亞歷山大港燈塔", "巴比倫空中花園", "羅德島太陽神巨像"]
            }
            var vm = avalon.define({
                $id: 'linkage',
                first: ["中國", "日本", "歐美"],
                second: map['日本'].concat(),
                third: map['日本武將'].concat(),
                firstSelected: "日本",
                secondSelected: "日本武將",
                thirdSelected: "織田信長"
            })


            vm.$watch("firstSelected", function (a) {
                vm.second = map[a].concat()
                vm.secondSelected = vm.second[0]
            })
            vm.$watch("secondSelected", function (a) {
                vm.third = map[a].concat()
                vm.thirdSelected = vm.third[0]
            })

        </script>
    </head>
    <body >
        <div ms-controller="linkage">
            <h3>下拉框三級聯動</h3>
            <select ms-duplex="@firstSelected" >
                <option  ms-for="el in @first" ms-attr="{value:el}" >{{el}}</option>
            </select>
            <select ms-duplex="@secondSelected" >
                <option  ms-for="el in @second" ms-attr="{value:el}" >{{el}}</option>
            </select>
            <select ms-duplex="@thirdSelected" >
                <option  ms-for="el in @third" ms-attr="{value:el}" >{{el}}</option>
            </select>
        </div>
    </body>
</html>

圖片描述
這里的技巧在於使用$watch回調來同步下一級的數組與選中項。注意,使用concat方法來復制數組。


免責聲明!

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



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