很easy的js双向绑定框架(二):控制器继承
初衷
上一篇已经实现了数据的双向绑定,但model的控制范围是整个文档。在实际project中必需要有作用范围,以便做ui模块的拆分。
这一篇,我们希望实现像angularjs一样的控制器继承:
1. 父controller的Model能够在子controller里被訪问到
2. 子controller的model不影响父controller
3. controller继承关系在html中指定。而不是js中指定
目标
html里,用isi-controller属性去声明控制器:
<body>
<div isi-controller="ParentController">
<input data-bind="name">
<div isi-controller="SubController">
<input data-bind="name">
</div>
</div>
</body
希望上面的input name 改了。以下的会跟着变,而以下的变了,上面的不变。
js里,用和上面isi-controller属性值同名的函数定义控制器:
function ParentController() {
var model = new Model();
model.set('name', 'parent');
}
function ParentController() {
var model = new Model();
model.set('name', 'sub');
}
对用户来说,仅仅要写这些。就完事儿了。
实现
版本号1
这个版本号採用最简单直观的思路:框架去找$(‘[isi-controller]’)的元素。然后给这些元素分别去绑定监听器、运行控制器函数
代码先列了:
index.html:
<html>
<head>
<title>simple MVVM</title>
<script src="js/ParentController.js"></script>
<script src="js/SubController.js"></script>
<script src="js/frame_v2.js"></script>
</head>
<body isi-controller="ParentController">
<input type="text" data-bind="name">
<div isi-controller="SubController">
<input type="text" data-bind="name">
</div>
</body>
</html>
ParentController.js:
function ParentController() {
var model = new Model();
model.set('name', 'parent');
}
SubController.js:
function SubController() {
var model = new Model();
model.set('name','sub');
}
frame_v2.js: (对比上一篇,主要修改在绑监听器和new Model的自己主动化)
var pubsub = ... //见上一篇
var Model = ... //见上一篇
// listener capture view changes --> publish model.change event
var changeHandler = function(event) {
var target = event.target,
propName = target.getAttribute('data-bind');
if( propName && propName !== '' ) {
pubsub.pub('model.change', propName, target.value);
}
event.stopPropagation();
}
/*----------- Init --------------*/
window.onload = function() {
/* first step:
* find controllers' dom
*/
var controllerRanges = document.querySelectorAll('[isi-controller]');
/* second step:
* bind listeners for each controllers' range,
* view.change event --> change each controllers' range
*/
for(var i=0, len=controllerRanges.length; i<len; i++) {
controllerRanges[i].addEventListener('change', changeHandler, false);
// view.change event --> change view
(function(index){
pubsub.sub('view.change', function(propName, newVal) {
var elements = controllerRanges[index].querySelectorAll('[data-bind=' + propName +']'),
tagName;
for(var i=0,l=elements.length; i<l; i++) {
tagName = elements[i].tagName.toLowerCase();
if(tagName==='input' || tagName==='textarea' || tagName==='select') {
elements[i].value = newVal;
} else {
elements[i].innerHTML = newVal;
}
}
});
})(i);
}
/* third step:
* execute each controller function
*/
for(var i=0, len=controllerRanges.length; i<len; i++) {
var controllerName = controllerRanges[i].getAttribute('isi-controller');
eval(controllerName+'()');
}
}
看看效果:
悲剧了。
没有实现第二个初衷:子控制器不影响父控制器。
这个问题该怎样解决呢?
版本号二,子不影响父
细致看代码,之所以会出现故障,是由于view.change信道的作用范围是有问题的。无论哪个model发出的view.change事件,两个控制器的view都会改变。
所以,我们给公布view.change事件的时候,多公布一个控制器名。好让接收view.change的时候知道应不应该修改html:
var Model = function(controllerName) {
var model = {
controllerName:controllerName,
props: {},
set: function(propName, value) {
this.props[propName] = value;
pubsub.pub('view.change', propName, value, this.controllerName); //就是这里!
}
}
控制器里new Model的时候注意把controller的名字初始化进去:
ParentController.js:
function ParentController() {
var model = new Model('ParentController');
model.set('name', 'parent');
}
最后接收view.change信道消息的时候,推断下controllerName:
pubsub.sub('view.change', function(propName, newVal, controllerName) {
....
thisControllerName = controllerRanges[index].getAttribute('isi-controller'),
if(thisControllerName !== controllerName)
return;
....
}
当然。监听器公布model.change的时候也是一样。要把控制器名称公布出去。代码就不贴了。
再看看效果:
妥了
全部代码:
github/victorisildur
很easy的js双向绑定框架(二):控制器继承的更多相关文章
- Vue.js双向绑定的实现原理和模板引擎实现原理(##########################################)
Vue.js双向绑定的实现原理 解析 神奇的 Object.defineProperty 这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.obser ...
- Vue.js双向绑定的实现原理
Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统.本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的.先讲涉及的知识点,再参考源码,用尽可能少 ...
- Vue.js双向绑定原理
Vue.js最核心的功能有两个,一个是响应式的数据绑定系统,另一个是组件系统.本文仅仅探究双向绑定是怎样实现的.先讲涉及的知识点,再用简化的代码实现一个简单的hello world示例. 一.访问器属 ...
- vue.js双向绑定之--select获取text
在大多数情况下select下拉菜单都是value和text设置不同的值的,value一般来说是与后台交互的值,而text是前端用来显示的文本: 但是,vue.js对到表单的双向绑定时如果option设 ...
- js 双向绑定
//双向绑定实例 <input name="" ng-bind-123="name" /> function DataBinder( object_ ...
- Angular js 双向绑定时字符串的转换成 数字类型的问题
问题: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <scrip ...
- js 双向绑定数据
let aaa = []; let bbb = [1,2,3]; let ccc = [0,9,8]; aaa = bbb; //此时aaa与bbb被绑定(aaa指向bbb的指向) ,若使用push则 ...
- js双向绑定和地址传递带来的痛苦解决方案之对象拷贝
function cloneObj(obj) { var newObj = {}; if (obj instanceof Array) { newObj = []; } for (var key in ...
- node.js面向对象实现(二)继承
http://blog.sina.com.cn/s/blog_b5a53f2e0101nrdi.html 继承是面向对象中非常重要的一个概念,那么在Node.js中如何实现继承呢? node.js在u ...
随机推荐
- system表空间爆满解决方法
分类: Oracle 问题描述: 对数据库做检查,发现system表空间持续占满99%.使用如下语句查看: SQL> select b.tablespace_name "表空间&q ...
- ylbtech-LanguageSamples-ComInteropPart2(COM 互操作第二部分)
ylbtech-Microsoft-CSharpSamples:ylbtech-LanguageSamples-ComInteropPart2(COM 互操作第二部分) 1.A,示例(Sample) ...
- 本地DNS如何解析公网域名
DNS服务器图解: 为了服务于公司内部局域网应用,如域.本地网站.论坛.OA.ERP系统等,我们通常搭建本地DNS服务器. 将本地DNS服务器设置为首选DNS,本地系统解析访问是快了. 但内部DNS怎 ...
- Centos7.4 建站系统和软件版本搭配
一.系统和软件版本搭配 版本: 1.1.2 类型: 建站系统 适用于: Centos7.4 64bit 集成软件版本: nginx_versi=1.12.2 PHP=7.1.13 (已提供提供Zend ...
- JavaScript获取table中某一列的值的方法
1.实现源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www. ...
- ibatis中使用List作为传入参数的使用方法及 CDATA使用
ibatis中list做回参很简单,resultClass设为list中元素类型,dao层调用: (List)getSqlMapClientTemplate().queryForList(" ...
- HTML5 Canvas 绘制库存变化折线 计算出库存周转率
<!DOCTYPE html> <html lang="utf-8"> <meta http-equiv="Content-Type&quo ...
- Python学习笔记(五)多进程实现并发服务器
每创建一个TCP连接,就创建一个进程. 代码如下: # coding: utf-8 import socket import os import sys import signal import ...
- Drawing-Order-Of-meshes-and-sprites
Sprite 和 Mesh Render 共存 [CanEditMultipleObjects] [CustomEditor(typeof(MeshRenderer))] public class M ...
- LogManager
public class LogManager { // Fields public static bool Debugstate; // Methods public static void Log ...