迷你MVVM框架 avalonjs 学习教程15、属性监听与模块通信
avalon的ViewModel对象从其内部EventManager里继承了三个方法,$watch、$unwatch、$fire三个方法,它们就是我们本节的主题。
词如其名,非常直白,一看就知道做什么。我们先从$watch方法说起,它能监听当前的VM第一层的监控属性 与 计算属性,如果某属性是一个对象,想监控其子孙属性,就需要定位到此对象上使用$watch回调了。$watch回调会默认传入先后两个属性值。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "2",
bbb: "2",
$ccc: "1",//这是非监控属性
ddd: "1",//这是非监控属性
$skipArray: ["ddd"],
click: function(a) {
model[a] = new Date - 0
}
})
model.$watch("aaa", function(a, b) {
console.log("aaa", a, b)
})
model.$watch("bbb", function(a, b) {
console.log("bbb", a, b)
})
model.$watch("$ccc", function(a, b) {
console.log("$ccc", a, b)
})
model.$watch("ddd", function(a, b) {
console.log("ddd", a, b)
})
</script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('bbb')">{{bbb}}</div>
<div ms-click="click('$ccc')">{{$ccc}}</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>
如果属性非常多,我们可以监听$all这个特殊的属性名来得知所有属性的变动状况。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "2",
bbb: "2",
$ccc: "1",
ddd: "1",
$skipArray: ["ddd"],
click: function(a) {
model[a] = new Date - 0
}
})
model.$watch("$all", function(name, a, b) {
console.log(name, a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('bbb')">{{bbb}}</div>
<div ms-click="click('$ccc')">{{$ccc}}</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>
我们也可以用$fire更改属性值。这样就可以打破不能触发非监控属性的回调的藩蓠,但要注意死循环,需要自己比较新旧值是否真的发生改变才触发。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "2",
bbb: "2",
$ccc: "1",
ddd: "1",
$skipArray: ["ddd"],
click: function(a) {
var old = model[a]
model.$fire(a, new Date - 0, old)
}
})
model.$watch("$all", function(name, a, b) {
console.log(name, a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('bbb')">{{bbb}}</div>
<div ms-click="click('$ccc')">{{$ccc}}</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>
注意,$watch回调里是用ecma262 v6 提供的新API Object.is做新旧值比较,它的功能与=== 差不多,但能对付NaN这个自己也不等于自己的怪胎。另,一个对象字面量即便外形看上去一致,也是一个新对象,不会等于原来的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
aaa: "1111",
nan: NaN,
object: {a: 1, b: 2},
array: [1, 2],
ddd: "1",
$skipArray: ["ddd"],
click: function(a) {
if (a == "object") {
model[a] = {a: 1, b: 2}
} else if (a == "array") {
model[a] = [1, 2]
} else if (a == "nan") {
model[a] = NaN
} else {
model[a] = "1111"
}
}
})
model.$watch("$all", function(name, a, b) {
console.log(name, a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('aaa')">{{aaa}}</div>
<div ms-click="click('nan')">{{nan}}</div>
<div ms-click="click('object')">
<div ms-repeat='object'>{{$key}}</div>
</div>
<div ms-click="click('array')">
<div ms-repeat='array'>{{el}}</div>
</div>
<div ms-click="click('ddd')">{{ddd}}</div>
</body>
</html>
对于数组,我们只能监听数组长度的变化,不能监听其内部是否发生变化。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var model = avalon.define({
$id: "test",
array: [1, 2],
click: function(a) {
model.array.push(new Date - 0)
}
})
model.array.$watch("length", function( a, b) {
console.log(a, b)
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('array')">
<div ms-repeat='array'>{{el}}</div>
</div>
</body>
</html>
如果你一定要监听数组每个元素的变化,可以使用1.3.4新添加的tick函数,这是一个心跳检测,只要函数返回false就会从检测列队中移除。由于是每30ms检测一次,非常耗性能,因此不用时记得移除。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="../avalon.js" ></script>
<script>
var ret
var model = avalon.define({
$id: "test",
array: [1, 2, 3, 4, 5, 6, 7, 8],
stop: function(){
ret = false
},
click: function(a) {
var index = Math.floor(Math.random() * 8)
model.array.set(index, new Date - 0)
}
})
var old = model.$model.array.concat()
avalon.tick(function() {
console.log("tick...")
var now = model.$model.array.concat()
for (var i = 0, n = now.length; i < n; i++) {
if (now[i] !== old[i]) {
console.log("第" + i + "个元素发生变化: " + old[i] + " --> " + now[i])
}
}
old = now
return ret
}) </script>
<style>
.ms-hover div:hover{
background:yellowgreen;
}
</style>
</head>
<body ms-controller="test" class='ms-hover'>
<div ms-click="click('array')">
<div ms-repeat='array'>{{el}}</div>
</div>
<button type='button' ms-click='stop'>移除此监听器</button>
</body>
</html>
稍微说一下 $unwatch的用法,这个不太常用。如果它传入两个参数,第一个为属性名,第二个为回调,那么就会移除此用户,如果只传入此属性名,则移除此属性的所有监听函数。如果什么也不传,那么就会临时中断此ViewModel的属性监听功能,所有$watch回调都不会触发。想恢复也很简单,调用$watch方法,也是什么也不传。
我们最后看一下1.3.2新增的跨模块通信功能,我们通过在$fire的第一个参数一些前缀,就能触发其他模块的属性回调。它们分别是”up!”, “down!”, “all!”。上与下是根据当前ViewModel所在ms-controller元素在DOM树位置决定的。
- up!xxx, 向上冒泡
- down!xxx, 向下捕获
- all!xxx, 全局广播
<!DOCTYPE html>
<html>
<head>
<title>by 司徒正美</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="avalon.js"></script>
<script>
avalon.define("ancestor", function(vm) {
vm.aaa = '1111111111'
vm.$watch("aaa", function(v) {
avalon.log(v)
avalon.log("ancestor.aaa事件被触发了")
})
vm.click = function() {
avalon.log("向下广播")
vm.$fire("down!aaa", "capture")
}
})
avalon.define("parent", function(vm) {
vm.text = "222222222"
vm.aaa = '3333333333'
vm.$watch("aaa", function(v) {
avalon.log(v)
avalon.log("parent.aaa事件被触发了")
})
vm.click = function() {
console.log("全局扩播")
vm.$fire("all!aaa", "broadcast")
}
})
avalon.define("son", function(vm) {
vm.$watch("aaa", function(v) {
avalon.log(v)
avalon.log("son.aaa事件被触发了")
})
vm.click = function() {
console.log("向上冒泡")
vm.$fire("up!aaa", "bubble")
}
})
</script>
</head>
<body class="ms-controller" ms-controller="ancestor">
<h3>avalon vm.$fire的升级版 </h3>
<button type="button" ms-click="click">
capture
</button>
<div ms-controller="parent">
<button type="button" ms-click="click">broadcast</button>
<div ms-controller="son">
<button type="button" ms-click="click">
bubble
</button>
</div>
</div>
</body>
</html>
迷你MVVM框架 avalonjs 学习教程15、属性监听与模块通信的更多相关文章
- 迷你MVVM框架 avalonjs 学习教程3、绑定属性与扫描机制
在MVVM框架中,你都会看到页面定了许多奇怪的属性,比如knockout的data-☆,angular的ng-☆,avalon的ms-☆,此外还有一些只写文本节点上的双花括号,它们统称为指令.ms-☆ ...
- 迷你MVVM框架 avalonjs 学习教程19、avalon历史回顾
avalon最早发布于2012.09.15,当时还只是mass Framework的一个模块,当时为了解决视图与JS代码的分耦,参考knockout开发出来. 它的依赖收集机制,视图扫描,绑定的命名d ...
- 迷你MVVM框架 avalonjs 学习教程20、路由系统
SPA的成功离开不这三个东西,分层架构,路由系统,储存系统.分层架构是我们组织复杂代码的关键,这里特指MVVM的avalon:路由系统是将多个页面压缩在一个页面的关键:储存系统特指本地储存,是安全保存 ...
- 迷你MVVM框架 avalonjs 学习教程18、一步步做一个todoMVC
大凡出名的MVC,MVVM框架都有todo例子,我们也搞一下看看avalon是否这么便宜. 我们先从react的todo例子中扒一下HTML与CSS用用. <!doctype html> ...
- 迷你MVVM框架 avalonjs 学习教程2、模块化、ViewModel、作用域
一个项目是由许多人分工写的,因此必须要合理地拆散,于是有了模块化.体现在工作上,PM通常它这为某某版块,某某频道,某某页面.某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑.在jQuery ...
- 迷你MVVM框架 avalonjs 学习教程1、引入avalon
avalon是国内最强大的MVVM框架,没有之一,虽然淘宝KISSY团队也搞了两个MVVM框架,但都无疾而终.其他的MVVM框架都没几个.也只有外国人与像我这样闲的架构师才有时间钻研这东西.我很早之前 ...
- 迷你MVVM框架 avalonjs 学习教程21、双向绑定链
avalon的双向绑定机制,是通过一条依赖链实现.此依赖链最底层是监控属性.监控数组,中层是计算属性.监控函数,再上点是求值函数,最上层是视图刷新函数. 所谓计算属性,监控属性,监控函数属性,我们改变 ...
- 迷你MVVM框架 avalonjs 学习教程12、数据联动
在许多表单应用,我们经常遇到点击一个复选框(或下拉框)会引发旁边的复选框(或下拉框)发生改变,这种联动效果用avalon来做是非常简单的.在avalon里,存在各种绑定回调与$watch回调,完全满足 ...
- 迷你MVVM框架 avalonjs 学习教程8、属性操作
属性操作是DOM操作很大的一块,它包括类名操作,表单元素的value属性操作,元素固有属性的管理,元素自定义属性的管理,某些元素的一些布尔属性的操作.大多数情况下,元素属性的值是字符串类型,我们称之为 ...
随机推荐
- Linux elasticsearch 安装 遇到的问题
备注:我的 Linux 测试机 是2G 内存的 ,估计内存小于 我的内存肯定会出这个问题 .(安装的最新版6.3.2) 1. 下载文件 解压 2 .试着 运行 bin 下面的 elasticse ...
- kibana 与 grafana
Kibana 和 Grafana 是两个开源工具,能可视化和推断大量日志数据内的趋势.Kibana 是一个分析和可视化平台,它可以让你浏览.可视化存储在Elasticsearch集群上排名靠前的日志数 ...
- linux 信号处理 四
一.信号生命周期 从信号发送到信号处理函数的执行完毕 对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生:信号在进程 ...
- 【ActiveMQ入门-9】ActiveMQ学习-与Spring集成2
概述: 下面将介绍如何在Spring下集成ActiveMQ. 消费者:同步接收: 目的地:Queue 环境: 共5个文件 Receiver.java ReceiverTest.java Sender. ...
- Apache JMeter配置、安装
一. 工具描述 apache jmeter是100%的java桌面应用程序,它被设计用来加载被测试软件功能特性.度量被测试软件的性能.设计jmeter的初衷是测试web应用,后来又扩充了其它的功能.j ...
- ESXI 5.5加载 zabbix OVF 3.2.6操作
如果是虚拟机安装ZABBIX,ZABBIX的前台WEB时间,是由虚拟机的BIOS时间决定的. 一. 1.去官方下载vmdk磁盘镜像 链接地址为https://sourceforge.net/proje ...
- tsql 循环id读取
declare @IDList as varchar(max) declare @ID as int declare @i as int set @IDList='' )) + ',' from ta ...
- 基于sklearn的 BaseEstimator开发接口:模型融合Stacking
转载:https://github.com/LearningFromBest/CMB-credit-card-department-prediction-of-purchasing-behavior- ...
- 让MySql支持Emoji表情(MySQL中4字节utf8字符保存方法)
手机端插入Emoji表情,保存到数据库时报错: Caused by: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\x84' ...
- 小朋友学Java(2):Win 7安装JDK
1 打开命令行窗口,输入java -version. 若提示不认识java命令,说明没有java环境. 1.png 2 从甲骨文网站(http://www.oracle.com/technetwo ...