TWaver HTML5 + Node.js + express + socket.io + redis(四)

原文出处:http://twaver.servasoft.com/?p=3764

 

 

接上一回TWaver HTML5 + Node.js + express + socket.io + redis(三), 这一篇您将了解到

1. 如何保存更改后的拓扑数据 (包括新增的, 修改的, 删除的)
2. 如何广播更改后的拓扑数据 (仅仅广播更改的数据)

下面是mac和iphone上的效果图, mac或iphone上的修改都将及时互相同步:

一. 先来看后台如何实现

后台需要做两件事情: 保存修改以及广播修改; 其中修改又分为是新增, 修改, 还是删除. 保存修改很容易, 无非就是对数据库的增删改, 广播数据也很容易, 调用Socket.emit之前, 先设置广播标记就ok了: Socket.broadcast.emit. 而且这个广播只会通知其他客户端, 发送这个广播的客户端不会收到广播, 这正好是我们需要的, 所以后台代码就好写了:

添加保存数据的socket.io监听, 里面保存数据后, 广播之:

//保存数据
client.on('saveData', function (datas) {
	if (!datas) {
		return;
	}
	//保存新增网元
	save(datas.add);
	//保存修改网元
	save(datas.change);
	//删除网元
	remove(datas.remove);
	//广播更新
	client.broadcast.emit('broadcast', datas);
});


保存数据的函数如下:

//保存网元
function save (datas) {
	if(!datas){
		return;
	}
	var elements = {};
	for (var i=0,n=datas.length,data; i<n; i++) {
		data = datas[i];
		elements[data.id] = JSON.stringify(data);
	}
	redis.hmset('datas', elements);
};

//删除网元
function remove (datas) {
	if(!datas){
		return;
	}
	var ids = [];
	for (var i=0,n=datas.length; i<n; i++) {
		ids.push(datas[i].id);
	}
	redis.hdel('datas', ids);
};


 

二. 前台实现

分两步: 监听数据的增删改并自动保存, 响应广播更新
1. 监听数据的增删改并自动保存

TWaver HTML5的数据模型提供了各种监听器, 以便在数据更改后做响应处理. 其中:
DataBox.addDataPropertyChangeListener用于监听数据容器里数据的属性变化, 也即对数据的修改, 其回调函数的参数包含属性: property(发生变化的属性), oldValue(旧值), newValue(新值), source(发生变化的数据)

DataBox.addDataBoxChangeListener用于监听数据容器的变化, 也即数据的添加, 删除以及清空, 其回调函数的参数包含属性: kind(容器变化类型, 可选值为add(新增), remove(删除), clear(清空)), data(新增或删除的数据), datas(清空的数据集合)

这里实现保存数据的思路是:
i> 如果有网元被修改, 则将被修改的网元加上change标记, 其中判断isChanging标志很重要, 因为需要用此标志区分是用户更改还是响应广播更新(也即程序更新)

//添加网元属性更改监听器
box.addDataPropertyChangeListener(function (e) {
	//如果正在响应广播更新,则返回
	if (isChanging) {
		return;
	}
	//如果属性为add, change 则返回
	if (e.property === 'C:add' || e.property === 'C:change') {
		return;
	}
	//设置保存数据标记
	needSave = true;
	//将有属性更改的节点置上change标记
	e.source.setClient('change', true);
});

ii> 如果有新增网元, 则将新增网元加上add标记, 如果有网元被删除, 则存入map中

//添加数据容器更改监听器
box.addDataBoxChangeListener( function (e) {
	//如果正在响应广播更新,则返回
	if (isChanging) {
		return;
	}
	//设置保存数据标记
	needSave = true;
	if (e.kind === 'add') {
		//将新增的节点置上add标记
		e.data.setClient('add', true);
	} else if (e.kind === 'remove') {
		//如果网元没有新增标记, 则存储到被删除网元列表中
		if (!e.data.getClient('add')) {
			//存储被删除网元
			removedElements[e.data.getId()] = e.data;
		}
	}
});


iii> 启动定时器, 保存更改
定时器代码如下:

//自动保存
function setAutoSave (value) {
	if(value){
		//定时器,每隔1秒钟保存修改
		timer = setInterval(function(){
			save();
		}, 1000);
	}else{
		window.clearTimeout(timer);
	}
}


 

保存数据代码如下, 需要注意的是, 得到要添加和被修改的网元后, 需要将其add和change标记置为false, 以避免重复保存:

//保存数据
function save () {
	//如果无数据添加、修改、删除,则直接返回
	if(!needSave){
		return;
	}

	var add = [];
	var change = [];
	var remove = [];
	isChanging = true;
	box.forEach(function(data){
		//新增的网元
		if(data.getClient('add')){
			add.push(toData(data));
		}
		//修改的网元
		else if(data.getClient('change')){
			change.push(toData(data));
		}
		//清除网元新增和修改标记
		data.setClient('add', false);
		data.setClient('change', false);
	});
	isChanging = false;
	//删除的网元
	for(var id in removedElements){
		remove.push(toData(removedElements[id]));
	}

	if(add.length == 0 && change.length == 0 && remove.length == 0){
		return;
	}

	//初始化待删除数据
	removedElements = {};
	//还原保存数据标记
	needSave = false;
	//构造更新数据
	var datas = {};
	if(add.length != 0){
		datas.add = add;
	}
	if(remove.length != 0){
		datas.remove = remove;
	}
	if(change.length != 0){
		datas.change = change;
	}
	socket.emit('saveData', datas);
}


从网元获取要持久化的数据代码如下:

//从网元获取持久化数据
function toData (element) {
	if(element instanceof twaver.Node) {
		return {
			id: element.getId(),
			name: element.getName(),
			location: element.getLocation()
		};
	} else {
		return {
			id: element.getId(),
			name: element.getName(),
			from: element.getFromNode() ? element.getFromNode().getId() : null,
			to: element.getToNode() ? element.getToNode().getId() : null
		};
	}
}


 

2. 响应广播更新

分为新增, 删除以及修改三种情况, 其中很重要的是在做这些事情之前先设置isChanging为true, 以防止重复广播更新

//响应广播更新
function onBroadcast (data) {
	if (!data) {
		return;
	}
	//设置响应广播更新标记,防止重复更新
	isChanging = true;
	var i, c, add=data.add, change=data.change, remove=data.remove;
	//添加节点
	if (add) {
		for (i=0,c=add.length; i<c; i++) {
			if (add[i].from) {
				addLink(add[i]);
			} else {
				addNode(add[i]);
			}
		}
	}
	//修改节点
	if (change) {
		for (i=0,c=change.length; i<c; i++) {
			changeData(change[i]);
		}
	}
	//删除节点
	if (remove) {
		for (i=0,c=remove.length; i<c; i++) {
			removeData(remove[i]);
		}
	}
	//还原更新广播数据
	isChanging = false;
}


增删改网元代码如下:

//添加节点
function addNode (data) {
	//构造节点
	var node = new twaver.Node(data);
	//添加节点
	box.add(node);
}

//添加节点
function addLink (data) {
	//查找from节点
	var from = box.getDataById(data.from);
	//查找to节点
	var to = box.getDataById(data.to);
	//构造连线
	var link = new twaver.Link({id: data.id, name: data.name}, from, to);
	//添加连线
	box.add(link);
}

//修改网元
function changeData (data) {
	var element = box.getDataById(data.id);
	//修改节点
	if (element instanceof twaver.Node) {
		element.setLocation(data.location.x, data.location.y);
		element.setName(data.name);
	}
	//修改连线
	else if (element instanceof twaver.Link) {
		element.setName(data.name);
		element.setFromNode(box.getDataById(data.from));
		element.setToNode(box.getDataById(data.to));
	}
	//网元被删除
	else {
		//网元被删除, 无需修改
	}
}

//删除网元
function removeData (data) {
	box.removeById(data.id);
}


 

最后, 附上本文的完整demo:TWaverHTML5Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值