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>



上面就是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;
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>


这里有一个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>


当我们点击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>

<!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>


通过操作属性就能操作视图是不是很爽呢!要知道上面的代码如果换成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>

此外,我们还可以通过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>

如果我们想控制对象属性的输出顺序,或让某些元素不输出来,那么我们可以使用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>

不过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>

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

迷你MVVM框架 avalonjs 学习教程11、循环操作的更多相关文章

  1. 迷你MVVM框架 avalonjs 学习教程19、avalon历史回顾

    avalon最早发布于2012.09.15,当时还只是mass Framework的一个模块,当时为了解决视图与JS代码的分耦,参考knockout开发出来. 它的依赖收集机制,视图扫描,绑定的命名d ...

  2. 迷你MVVM框架 avalonjs 学习教程18、一步步做一个todoMVC

    大凡出名的MVC,MVVM框架都有todo例子,我们也搞一下看看avalon是否这么便宜. 我们先从react的todo例子中扒一下HTML与CSS用用. <!doctype html> ...

  3. 迷你MVVM框架 avalonjs 学习教程3、绑定属性与扫描机制

    在MVVM框架中,你都会看到页面定了许多奇怪的属性,比如knockout的data-☆,angular的ng-☆,avalon的ms-☆,此外还有一些只写文本节点上的双花括号,它们统称为指令.ms-☆ ...

  4. 迷你MVVM框架 avalonjs 学习教程1、引入avalon

    avalon是国内最强大的MVVM框架,没有之一,虽然淘宝KISSY团队也搞了两个MVVM框架,但都无疾而终.其他的MVVM框架都没几个.也只有外国人与像我这样闲的架构师才有时间钻研这东西.我很早之前 ...

  5. 迷你MVVM框架 avalonjs 学习教程22、avalon性能大揭密

    avalon之所以能在页面处理1W个绑定(angular对应的数字是2000),出于两个重要设计--基于事件驱动的双向绑定链及智能CG回收机制. avalon的双向绑定链是通过Object.defin ...

  6. 迷你MVVM框架 avalonjs 学习教程20、路由系统

    SPA的成功离开不这三个东西,分层架构,路由系统,储存系统.分层架构是我们组织复杂代码的关键,这里特指MVVM的avalon:路由系统是将多个页面压缩在一个页面的关键:储存系统特指本地储存,是安全保存 ...

  7. 迷你MVVM框架 avalonjs 学习教程16、过滤器

    avalon的过滤器是参考自angular与rivets.它也被称做管道文本过滤器,它的处理对象只能是文本(字符串),它只能用在文本绑定中,并且只能是双花括号形式.下面是各大家的过滤器比较: rive ...

  8. 迷你MVVM框架 avalonjs 学习教程4、数据填充

    MVVM是前端的究极解决方案,你们可能用过jQuery,但那个写的代码不易维护:你们可以听过说requirejs与seajs,传说中的模块开发,加载器,但它们的最终目标是打包:你们可能听过unders ...

  9. 迷你MVVM框架 avalonjs 学习教程2、模块化、ViewModel、作用域

    一个项目是由许多人分工写的,因此必须要合理地拆散,于是有了模块化.体现在工作上,PM通常它这为某某版块,某某频道,某某页面.某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑.在jQuery ...

随机推荐

  1. SQL SERVER 2008 彻底卸载干净方法 (转)

    最近安装SQL SERVER 2008失败后,再重新安装时老是报错,东搞西搞的很难卸干净.但又不方便重装系统,经按下面方法终于搞定并成功安装上2008 1.停掉SQL SERVER 2008所有相关服 ...

  2. R(5): sql 数据处理

    sqldf程序包是R语言中实用的数据管理辅助工具,但最新版本的包在处理中文时出现乱码,待解决 Usage:  sqldf(x, stringsAsFactors = FALSE,  row.names ...

  3. 【python】序列切片和range函数

    序列的每个元素都可以用2种索引的表达方式,一种是正数索引,另一种是负数索引. 序列切片,作用是访问序列中一定范围的元素,格式“序列名[A:B]”,其中A为所切片的第一个元素的索引号,而B为切片后剩下的 ...

  4. linux raid10管理维护

    http://www.linuxidc.com/Linux/2015-10/124391.htm    制作raid10 http://www.linuxidc.com/Linux/2015-09/1 ...

  5. 异步FIFO跨时钟域亚稳态如何解决?

    跨时钟域的问题:前一篇已经提到要通过比较读写指针来判断产生读空和写满信号,但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域 ...

  6. [模板] Miller_Rabin素数判断代码实现存档

    就是....存存代码吧. Miller_Rabin的最核心部分在于二次探测定理和费马小定理.后者在同余/逆元的题目里面或多或少都有提及吧.....前者也很简单. 总而言之,Miller_Rabin不算 ...

  7. Mybatis -代码自动生成(generatorConfig.xml)

    参考:http://blog.csdn.net/jinshiyill/article/details/51546676 官方网址: http://www.mybatis.org/generator/c ...

  8. Android RILD运行机制详解

    前言 在上一篇文章里(http://blog.csdn.net/jason_wzn/article/details/53232022),简要介绍了Android RIL的架构.这一篇文章,就来看一看R ...

  9. Microsoft Dynamics CRM 2011 安装完全教程

    作者:卞功鑫,转载请保留.http://www.cnblogs.com/BinBinGo/p/4302612.html 环境介绍 WINDOWS 2008 R2 Datacenter Microsof ...

  10. 第3课 进化后的 const分析

    1.  C语言中的const (1)const修饰的变量是只读的,使得变量具有只读属性,但本质还是变量.所以不是真正的常量,它只是告诉编译器该变量不能出现在赋值符号的左边. (2)const修饰的局部 ...