迷你MVVM框架 avalonjs 学习教程12、数据联动

在许多表单应用,我们经常遇到点击一个复选框(或下拉框)会引发旁边的复选框(或下拉框)发生改变,这种联动效果用avalon来做是非常简单的。在avalon里,存在各种绑定回调与$watch回调,完全满足你的需求。

下面是avalon所有绑定回调。

  1. data-duplex-changed,用于ms-duplex绑定,值改变后触发; 参数:当前元素的值
  2. data-include-loaded,用于ms-include-src绑定,模板加载后触发,可以在这里修改模板 参数: tmpl,vmodel1, vmodel2 ...
  3. data-include-rendered,用于ms-include, ms-include-src绑定,模板渲染好后触发; 没参数
  4. data-repeat-rendered,用ms-repeat绑定,当监控数组发生添加,删除,重排等操作时触发; 参数:当前操作名("add","del","index","clear","move")
  5. data-with-sorted,用ms-repeat, ms-with绑定,赶对象渲染之前触发,要求输出一个字符串数组,对象的键值对会根据它依次输出; 参数:原对象的所有键名构成的数组
  6. data-with-rendered,用ms-with绑定,当目标对象输出页面后触发; 参数:当前操作名("add","del","index","clear","move")
  7. data-each-rendered,用ms-each绑定,当监控数组发生添加,删除,重排等操作时触发; 参数:当前操作名("add","del","index","clear","move")

在表单联动效果中,我们最常用的是data-duplex-change。事不宜迟,我们先来一个全选非全选例子吧。

<!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 = 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 我们仔细分析其源码,allchecked是用来控制最上面的复选框的打勾情况,数组中的checked是用来控制下面每个复选框的下勾情况。由于是使用ms-duplex,因此会监听用户行为,当复选框的状态发生改变时,就会触发data-duplex-change回调,将当前值传给回调。但这里我们不需要用它的value值,只用它的checked值。最上面的复选框对应的回调是checkAll,它是用来更新数组的每个元素的checked属性,因此一个forEach循环赋值就是。下面的复选框对应的checkOne,它们是用来同步最上面的复选框,只要它们有一个为false上面的复选框就不能打勾,当它们被打勾了,它们就得循环整个数组,检查是否所有元素都为true,是才给上面的checkall属性置为true。

再看用ms-duplex与$watch实现表格排序效果。

<!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="../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}

                ]

            })

            model.$watch("selected", function(v) {

                var t = parseFloat(model.trend)

                model.data.sort(function(a, b) {

                    if (v === "name") {

                        return t * a[v].localeCompare(b[v])

                    } else {

                        var ret = a[v] > b[v] ? 1 : -1

                        return t * ret

                    }

                })

            })

            model.$watch("trend", function(t) {

                var v = model.selected, t = parseFloat(t)

                model.data.sort(function(a, b) {

                    var ret = a[v] > b[v] ? 1 : -1

                    return t * ret

                })

            })



        </script>

    </head>

    <body ms-controller="test">

        <div style="color:red">

            <p>本例子用于显示如何做一个简单的表格排序</p>

            <p>ms-repeat="array"相当于ms-repeat-el="array" </p>

        </div>

        <p>

            <select ms-duplex="selected">

                <option  ms-repeat="options">{{el}}</option>

            </select>

            <select ms-duplex="trend">

                <option value="1">up</option>

                <option value="-1">down</option>

            </select>

        </p>

        <table width="500px" border="1">

            <tbody >

                <tr ms-repeat="data">

                    <td>{{el.name}}</td> <td>{{el.size}}</td> <td>{{el.date}}</td>

                </tr>

            </tbody>

        </table>

    </body>

</html>

enter image description here 当我们改动下拉框时,会通过ms-duplex同步selected, trend属性,而selected, trend的值改变时,就会触发对应的$watch回调,然后通过监控数组的sort方法实现表格排序。从而也可以看出监控数组与ms-repeat的强大。

我们再来一个文本域与下拉框的联动例子,它只用到ms-duplex,不过两个控件都是绑定同一个属性。

<!DOCTYPE html>

<html>

    <head>

        <script src="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-repeat-option="options" ms-value="option" >{{option}}</option>

        </select>

    </body>

</html>

enter image description here

我们再看一个超级复杂的三级联动下拉框。

<!DOCTYPE html>

<html>

    <head>

        <script src="avalon.js"></script>

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

        <script>

            var map = {

                "中国": ["江南四大才子", "初唐四杰", "战国四君子"],

                "日本": ["日本武将", "日本城堡", "幕府时代"],

                "欧美": ["三大骑士团", "三大魔幻小说", "七大奇迹"],

                "江南四大才子": ["祝枝山", "文征明", "唐伯虎", "周文宾"],

                "初唐四杰": ["王勃", "杨炯", "卢照邻", "骆宾王"],

                "战国四君子": ["楚国春申君黄歇", "齐国孟尝君田文", "赵国平原君赵胜", "魏国信陵君魏无忌"],

                "日本武将": ["织田信长", "德川家康", "丰臣秀吉"],

                "日本城堡": ["安土城", "熊本城", "大坂城", "姬路城"],

                "幕府时代": ["镰仓", "室町", "丰臣", "江户"],

                "三大骑士团": ["圣殿骑士团", "医院骑士团", "条顿骑士团"],

                "三大魔幻小说": ["冰与火之歌", "时光之轮", "荆刺与白骨之王国"],

                "七大奇迹": ["埃及胡夫金字塔", "奥林匹亚宙斯巨像", "阿尔忒弥斯月神殿", "摩索拉斯陵墓", "亚历山大港灯塔", "巴比伦空中花园", "罗德岛太阳神巨像"]

            }



            avalon.define("linkage", function(vm) {

                vm.first = ["中国", "日本", "欧美"]

                vm.second = map[vm.first[1]].concat()

                vm.third = map[vm.second[0]].concat()

                vm.firstSelected = "日本"

                vm.secondSelected = "日本武将"

                vm.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-repeat="first" ms-value="el" >{{el}}</option>

            </select>

            <select ms-duplex="secondSelected" >

                <option ms-repeat="second" ms-value="el" >{{el}}</option>

            </select>

            <select ms-duplex="thirdSelected" >

                <option ms-repeat="third" ms-value="el" >{{el}}</option>

            </select>

        </div>

    </body>

</html>

enter image description here 由于存在三个下拉框,需要的数据比较多,因此我们搞了一个map来存放它们。。然后我们先初始化第一个下拉框,vm.first = [”中国”, ”日本”, ”欧美”],默认是选中第二个(firstSelected)。然后初始化第二个下拉框,需要从map拷贝一份赋给second,然后再默认其选中项,然后是第三个下拉框……

接着是通过$watch回调实现联动,不用管第三个下拉框(因为它总是被动的),那么只有监听firstSelected, secondSelected。每次变化都需要从map找到正确的数组,复制一次赋给second与third,并且默认选中第一项。

最后奉上一用户做的多级联动效果,自己细细品味吧。

<!DOCTYPE html>

<html lang="zh-cn">

    <head>

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

        <script src="avalon.js"></script>

        <script>



            var data = [

                {"id": 0, "name": "1公司", "grade": -1, "parentid": "-1"},

                {"id": 1, "name": "广东公司", "grade": 0, "parentid": "0"},

                {"id": 2012300001, "name": "广州1分公司", "grade": 1, "parentid": 1},

                {"id": 200000000, "name": "广州分公司", "grade": 1, "parentid": 1},

                {"id": 4050, "name": "天河分公司", "grade": 2, "parentid": 200000000},

                {"id": 999182, "name": "工业园营销服务中心", "grade": 3, "parentid": 4050},

                {"id": 4174, "name": "南沙分公司", "grade": 2, "parentid": 200000000},

                {"id": 10121, "name": "南沙营销服务中心", "grade": 3, "parentid": 4174},

            ]

            var a = avalon.define("test", function(vm) {

                vm.searchForm = [];

                vm.setSearchFormInit = function(currNode) {

                    var childNodes = vm.getChildOrg(currNode);

                    if (childNodes.length)

                        vm.searchForm = [childNodes];

                };

                vm.setSearchForm = function(index) {

                    //vm.searchForm._del(index,10)//在选中某个select后,取得index,删除它后边的所有成员

                    avalon.log("删除第" + index + "个元素后的所有成员")

                    a.searchForm.removeAll(function(arr, i) {

                        if (i > index)

                            return true;

                        return false;

                    });

                    var currNode = this.value;

                    var childNodes = vm.getChildOrg(currNode);

                    if (childNodes.length)

                        vm.searchForm.push(childNodes);

                };

                vm.getChildOrg = function(id) {

                    var retList = [];

                    for (var i = 0; i < data.length; i++) {

                        var elem = data[i]

                        if (elem.parentid != id)

                            continue;

                        retList.push({

                            id: elem.id,

                            name: elem.name,

                            parentid: elem.parentid

                        });

                    }

                    return retList;

                }

            })

            a.setSearchFormInit(0)

        </script>

    </head>

    <body ms-controller="test">

        <div ms-repeat-form="searchForm">

            <select ms-change="setSearchForm($index)">

                <option></option>

                <option ms-repeat-el="form" ms-value="el.id">{{el.name}}</option>

            </select>

        </div>

    </body>

</html>

enter image description here

你可能感兴趣的:(val)