avalon的一个显著优点是它将视图与业务逻辑完全分离开来。若不用avalon的话,举个例子,当我们从后台获取数据后,通常还要自行对数据通过jquery等方式对数据进行处理,然后通过相应调整使该数据在DOM显示出来。而当使用了avalon后,对数据的操作和DOM的展示就可以很好的分离开来,通过VM实现对数据的操作,通过DOM实现数据的展示,然后对数据的操作会通过AVALON的库动态的传递到DOM,动态修改DOM展示内容。
1.vm的定义
该模型通过avalon.define方式进行定义,简称vm,avalon中用户定义的所有vm都会在avalon.vmodels中存储,因此为了方便获取,每个vm都通过$id来进行标识。ms-controller用来说明对于该vm的调用以及调用后的作用域。
如
<div ms-controller="hello">
<h1>Hello, {{name}}</h1>
</div>
说明想要在标题h1中调用$id为hello的vm
该模型定以后规定不许再添加新属性和方法,但可以添加子属性。比如以下是错误的添加属性方法。
以下为正确的添加子属性方法
其中placehoder是预先初始化已经定义好的,因此,可以通过该对象来添加子属性。
对vm进行定义=除了以上代码,还可以使用下面的形式
avalon.define("blog", function(vm){
vm.subject = "Blog Title";
vm.author = "张三";
vm.publish_date = new Date('2014/3/15');
vm.comment_count = 0;
vm.content = "<h2>这是一个测试的博客</h2>" +
"<p>这是内容 1</p>" +
"<p>这是内容 2</p>" +
"<p>这是内容 3</p>" +
"<p>这是内容 4</p>"
;
});
2.在MVVM中vm(view model)、view和model
在该框架中,model其实就是所分析的数据对象,通常从数据库映射而来。view‘就是所显示的用户界面,本人理解,就是DOM方面的展示。而view model则是view与model间的桥梁,由于数据库很难直接跟网页控件相联系,所以需要VM在中间来指明view所对应的model。
3. 对ms-duplex属性的理解
在第1部分我们提到,通过avalon,可以将对数据的操作动态的传递到DOM,动态修改DOM展示内容,ms-duplex便是该功能实现所需的重要属性。举一个例子
< title >Avalon by RubyLouvre</ title > |
< meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" > |
< script src = "avalon.js" ></ script > |
< div ms-controller = "test" > |
< input ms-duplex = "string" >{{string}} |
< input ms-duplex = "bool" type = "radio" >{{bool}} |
< input ms-duplex = "number" >{{number}} |
< li ms-repeat = "object" >{{$key}} --> {{$val}}</ li > |
在
<
input
ms-duplex
=
"string"
>{{string}}部分,ms-duplex的作用,就是将该input控件中的value值重新赋给VM中的string(通过ms-duplex="string"来指定),作为string的新值,覆盖原来的XXX.此时{{string}}中显示的内容动态变为input控件输入的string对象的新值,而非XXX.在这里读者可能会问,avalon框架的一个特性不就是动态更新页面吗,即使不适用ms-duplex也可以做到通过input控件中的值value更新vm中的对象值进而动态更改视图显示啊,恩~这么说吧,如果不使用ms-duplex,则需要在input控件内将value值录入后一次性通过click等事件以及在VM中定义的函数进行关联,从而提交value,从而更新vm中的对象,而若使用了ms-duplex,则在控件中每输入一个字符,该字符就动态更新vm中的个对象,再输入一个字符,再使用控件中的value动态更新vm中的对象,无需再在VM中定义功能为修改VM对象值的函数,也不用一定要死板的需要全部输入完后通过事件来提交所更改的值。
4.有关于监控数组的代码1(注释部分为本人理解,在该部分目前由于本人为初学阶段,可能很多关于功能的理解并不正确,见谅)
<html>
<head>
<title>Avalon by RubyLouvre</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="avalon.js" ></script>
<script>
var data = {
$id: "array",
array: ["1", "2", "3", "4"],
removeAt: function(e) {
if (isFinite(this.value) && e.which == 13) { //this为input元素
var a = ~~this.value
vm.array.removeAt(a)
this.value = "";
}
}
}
"push,unshift,remove,ensure".replace(/\w+/g, function(method) {
<!--判断ms-keypress和ms-click获取的字符串为push/unshift/remove/ensure中的哪个字符串,method为该字符串-->
data[method] = function(e) {
<!--通过data[method]处,将该段代码与data相关起来,从而在后面可以通过avalon.define(data)定义-->
if (this.value && e.which == 13) {
<!--this为视图中input框中元素,e为对事件的捕捉,e.which==13表示键入回车键-->
vm.array[method](this.value);
<!--数组名[push]()为一个数组类型的方法,功能为向数组中添加元素-->
this.value = "";
}
}
})
"pop,shift,sort,reverse".replace(/\w+/g, function(method) {
data[method] = function(e) {
vm.array[method]();
}
})
var vm = avalon.define(data)
<!--avalon1.5这样定义VM,这样就将var data 和""push...""这段相关于data的vm定义了-->
</script>
</head>
<body>
<fieldset ms-controller="array">
<ul>
<li ms-repeat="array">数组的第{{$index+1}}个元素为{{el}}</li>
</ul>
<div>对数组进行push操作,并回车
<input ms-keypress="push">
</div>
<div>
<button type="button" ms-click="pop">对数组进行pop操作</button>
</div>
</fieldset>
</body>
</html>
5.有关数组监控的代码2
<html>
<head>
<title>Avalon by RubyLouvre</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="avalon.js" ></script>
<script>
var vm2 = avalon.define({
$id: "c-collection2",
selected: "name",
options: ["name", "size", "date"],
trend: 1,
data: [{
name: "aaa",
size: 213,
date: Date.now() + 20
}, {
name: "bbb",
size: 4576,
date: new Date - 4
}, {
name: "ccc",
size: 563,
date: new Date - 7
}, {
name: "eee",
size: 3713,
date: 9 + Date.now()
}, {
name: "555",
size: 389,
date: Date.now() - 20
}]
})
vm2.$watch("selected", function(v) {
//视图名.$watch(object_name,function(){})用于监听指定vm中名为object_name属性的变化,一旦属性值发生变化,则执行function函数,在本程序中,v为selected变化后的取值。
var t = parseFloat(vm2.trend)
vm2.data.sort(function(a, b) {
var ret = a[v] > b[v] ? 1 : -1
return t * ret
})
//array.sort(function())函数中function()决定了array按什么进行排序,具体内容参照js中数组的sort方法。
})
vm2.$watch("trend", function(t) {
var v = vm2.selected,
t = parseFloat(t)
vm2.data.sort(function(a, b) {
var ret = a[v] > b[v] ? 1 : -1
return t * ret
})
})
</script>
</head>
<body>
<fieldset ms-controller="c-collection2">
<legend>示例二</legend>
<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>
</fieldset>
</body>
</html>
6.计算属性$computed
计算属性的作用是通过其他两个或多个监控属性计算而得到一个新的值。想要通过计算属性获取值的对象必须要含有set和get对象。
通过以下这个例子来介绍计算属性的使用方法
avalon.define({
$id: "test",
firstName: "司徒",
lastName: "正美",
name:{ //name即为监控属性,一个包含set或get的对象
set: function(val) {//里面必须用this而不能使用vm,问什么?我也不知道。。。。,val值为vm中所定义的对象firstName与lastName相加所得字符串
var array = (val || "").split(" ");//array为val或者“”通过“”划分后所得字符串数组
this.firstName = array[0] || "";
this.lastName = array[1] || "";
},
get: function() {
return this.firstName + " " + this.lastName;,//通过该段代码指定fullName的值为this.firstName + " " + this.lastName }
}
})
}
7.非监控属性
非监控属性前面加$符号,如$id,$用户自定义属性名。非监控属性与监控属性的区别是对非监控属性值的更改不会动态在视图上同步出来。如以下代码
< title >unobservable</ title > |
< meta name = "viewport" content = "width=device-width" > |
< script src = "avalon.js" ></ script > |
$skipArray: ["bbb", "ccc"], |
vm.$aaa = vm.aaa = vm.bbb = vm.ccc = "change" |
< body ms-controller = "test" > |
< p >{{$aaa}}</ p ><!--其中vm中$aaa属性值虽然变化了,但在视图上显示的仍然为sss,而非现在的change。--> |
<!--该值在视图上随着click函数的运行,动态改变。-->
< p >{{ccc}}</ p ><!--由于$skipArray属性,bbb和ccc也成为了非监控属性。--> |
< button type = "button" ms-click = "click" >点我</ button > |
<!--通过ms-click='click'运行了click函数-->
8、ms-class
avalon中对于标签的类的定义使用ms-class="类名:表达式",其中“:表达式”部分可写可不写,若表达式为true,则标签按照代码指定为指定类,否则相当于没有该段指定类的代码,为之前的默认类。,而除了通过ms-class指定类,还可通过ms-class-1、ms-class-2.....等也可实现对类的指定。当一个标签需要在不同情况下指定不同类,就可以同时使用ms-class、ms-class-1等来实现。不可以在一个标签内使用两次ms-class。代码实例如下:
< body ms-controller = "ms-class" > |
< div class = "test" ms-class = "aaa:toggle" ms-click = "click" >点我</ div > |
<!--ms-class指定类的优先级高于test,该div标签内容首先以aaa格式显示,若toggle为false,则以test格式显示-->
<ul ms-repeat="object">
<li ms-class="first:$first" ms-class-1="last:$last"><!--将表示数组第一个元素和最后一个元素的li标签分别指定为first类和last类。-->:
{{el.name}}:{{el.value}}
</li>
</ul>
对类指定后,则该标签内容在视图显示格式为对应的css所定义的.类名的格式
9、avalon作用域
为了实现不同人员的相互协作,每个人负责不同的avalon视图模型的编写是理所当然的,那么在多个视图模型存在的情况下,我们必然会涉及到对不同视图模型的作用域的定义,即该模型在视图代码的何处生效。
负责VM作用域定义的属性有三个:ms-controller,ms-important,ms-skip
其中ms-controller和ms-important类似,唯一的区别就是当ms-controller所指定区域的html代码含有该VM未定义的对象时,该html代码可向上寻找,寻找上一级即作用域包含了包含该区域的VM,而ms-important则不会向上寻找。为了更好的说明,我们来举一个例子:
一段代码,其VM的定义代码如下:
avalon.ready( function () { |
html代码如下:
< div ms-controller = "AAA" > |
< div >{{name}} : {{color}}</ div > |
< div ms-controller = "BBB" > |
< div >{{name}} : {{color}}</ div > |
< div ms-controller = "CCC" > |
< div >{{name}} : {{color}}</ div > |
< div >{{name}} : {{color}}</ div > |
其在浏览器的显示结果为
liger : green
说明在最后一个标签处,名为DDD的VM中没有color对象,则直接显示出{{color}},而倒数第二个标签,当CCC中无color对象时,会向上寻找到上一级的名为BBB的VM,显示出该VM种的color值。
10.avalon过滤器
avalon中定义过滤器的方式为:avalon.filters.过滤器名=function(第一个参数,其余参数...){函数定义}.代码示例
avalon.filters.haha = function (str) { |
<!---此过滤器的作用是通过{{变量名|haha}}来得到字符串aaa哈哈。---
>
在html的显示代码中,对该过滤器的调用代码如下:
<p>{{aaa|haha}}</p>
<!--假设在avalon的vm中aaa对象的值为“加油”,则此处的作用就是在页面上显示出“加油哈哈”--->