前端MVC—基于路径的数据层(PathStore)

本文介绍了一种前端MVC架构中的数据层组件实现方法,该组件能够通过对象路径操作对象,适用于处理复杂的表单并将数据转化为对象。文章详细解释了如何使用PathStore类进行数据的插入、更新、获取及删除等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(function(){
/**
 * 拷贝数组,不拷贝undefined元素
 */
function cloneArray(source) {
	var prop = null,
		i = 0,
		len = source.length,
		target = [];
	for(; i < len; i++) {
		prop = source[i];
		if(typeof prop === "undefined") {
			continue;
		}
		if(YAHOO.lang.isObject(prop)) {
			target[target.length] = clone(prop);
		} else if(YAHOO.lang.isArray(prop)) {
			target[target.length] = cloneArray(prop);
		} else {
			target[target.length] = source[i];
		}
	}
	return target;
}

/**
 * 复制对象的内容
 * @returns {Object} 返回新对象
 */

function clone(source) {
	var prop = null,
		target = {}, 
		arr = [];
	if(YAHOO.lang.isArray(source)) {
		return cloneArray(source);
	} 
	
	for(var i in source) {
		if(source.hasOwnProperty(i)) {
			prop = source[i];
			if(typeof prop === "undefined") {
				continue;
			}
			if(YAHOO.lang.isObject(prop)) {
				target[i] = clone(prop);
			} else if(YAHOO.lang.isArray(prop)) {
				target[i] = cloneArray(prop);
			} else {
				target[i] = source[i];
			}
		}
	}
	return target;
}

function isEmpty(obj) {
	return (L.isNull(obj) || L.isUndefined(obj));
}

/**
 * 前端MVC的数据层,通过对象路径操作对象。
 * 这个组件一般用于处理复杂的表单,将表单项与后台对象的路径一一对应起来,直接将前台的表单数据转化为对象。
 * @author cwb0525@gmail.com
 * @example
 * var json = {a: 1, b: 'test', c: [1, 3]};
 * var store = new PathStore(json)
 * store.put("/a", 2);
 * store.put("/c/0", 33);
 * console.log("%o", store);
 */

var PathStore = function(root) {
	if(root) {
		this.root = root;
	}
};


PathStore.prototype = {
	root: {},
	initialize: function(root){
		if(root) {
			this.root = root;
		}
	},
	/**
	 * @param {String} path /a/b/c
	 * @param {Object} value
	 */
	insert: function(path, value, force){
		var parts = path.split("/"),
			space = parts.shift(),
			i = 0,
			part = null,
			len = parts.length,
			curr = this.root,
			maxIndex = len - 1,
			idx = 0;
		if(force) {
			this.put(path, value);
		}
		do{
			part = parts[i];
			if(isEmpty(curr)) {
				throw "不存在的路径:" + path;
			}
			
			//对应的情况:/a/b/c/
			if(maxIndex === i && part.length === 0) {
				if(!YAHOO.lang.isArray(curr)) {
					throw path + "不是一个数组";
				}
				idx = curr.length;
				curr[idx] = value;
				return idx;
			}
			if(maxIndex === i && part.length > 0) {
				curr[part] = value;
			}
			i += 1;
			
			curr = curr[part];
		} while(i < len);
	},
	/**
	 * 强制更新,路径不存在则创建
	 */
	put: function(path, value) {
		console.log("put --- " + path + ":" + value);
		var parts = path.split("/"),
			space = parts.shift(),
			i = 0,
			part = null,
			len = parts.length,
			curr = this.root,
			parent = null,
			idx = 0,
			regNum = /^[1-9]\d*|0$/;
		
		for(var i = 0; i < len - 1; i++) {
			part = parts[i];
			if(isEmpty(curr[part])) {
				//下一个键值名一个整数
				if(regNum.test(parts[i + 1])) {
					curr[part] = [];
				} else {
					curr[part] = {};
				}
			}
			curr = curr[part];
		}
		part = parts[i];
		if(L.isArray(curr)) {
			idx = parseInt(part);
			if(idx !== NaN) {
				curr[idx] = value;
			} else {
				throw "路径无效:" + path;
			}
		} else {
			curr[part] = value;
		}
		
	},
	
	update: function(path, value){
		var parts = path.split("/"),
			space = parts.shift(),
			i = 0,
			part = null,
			len = parts.length,
			curr = this.root,
			maxIndex = len - 1,
			parent = null,
			idx = 0;
			
		do{
			part = parts[i];
			//对应的情况:/a/b/c/
			if(maxIndex === i && part.length === 0) {
				if(!YAHOO.lang.isArray(parent)) {
					console.log(path + "不是一个数组");
					return false;
				}
				idx = parseInt(part);
				if(idx === NaN) {
					console.log("非法的数组路径:" + path);
					return false;
				}
				parent[idx] = value;
			}
			
			if(maxIndex === i && part.length > 0) {
				curr[part] = value;
				return true;
			}
			i += 1;
			parent = curr;
			curr = curr[part];
			
			if(isEmpty(curr)) {
				console.log("不存在的路径:" + path);
				return false;
			}
		} while(i < len);
	},
	
	remove: function(path) {
		var parts = path.split("/"),
			space = parts.shift(),
			i = 0,
			part = null,
			len = parts.length,
			curr = this.root,
			maxIndex = len - 1,
			parent = null;
			
		do {
			part = parts[i];
			
			//对应的情况:/a/b/c/
			if(maxIndex === i && part.length === 0) {
				if(!YAHOO.lang.isArray(curr)) {
					console.log("尝试删除非法路径:" + path);
					return false;
				}
				while(curr.length) {
					curr.pop();
				}
				break;
			}
			
			if(maxIndex === i && part.length > 0) {
				if(YAHOO.lang.isArray(curr)) {
					part = parseInt(part);
				} 
				if(typeof curr[part] === "undefined") {
					console.log("尝试删除不存在的路径:" + path);
					return false;
				}
				delete curr[part];
				return true;
			}
			
			i += 1;
			curr = curr[part];
			parent = curr;
			
			if(isEmpty(curr)) {
				console.log("尝试删除不存在的路径:" + path);
				return false;
			}
		} while(i < len);
	},
	
	get: function(path) {
		var parts = path.split("/"),
			space = parts.shift(),
			i = 0,
			part = null,
			len = parts.length,
			curr = this.root,
			maxIndex = len - 1;
			
		do {
			part = parts[i];
			//对应的情况:/a/b/c/
			if(maxIndex === i && part.length === 0) {
				if(!YAHOO.lang.isArray(curr)) {
					return null;
				}
				return curr;
			}
			
			if(isEmpty(curr) || isEmpty(curr[part])) {
				console.log("get --- 不存在的路径: " + path);
				return null;
			}
			
			if(maxIndex === i && part.length > 0) {
				if(YAHOO.lang.isArray(curr)) {
					part = parseInt(part);
				} 
				return curr[part];
			}
			
			i += 1;
			curr = curr[part];
		} while(i < len);
		
		return null;
	},
	/**
	 * @param {Object} handlers 特殊域的处理函数集合
	 */
	updateFromForm: function(form, handlers) {
		var elems = form.elements || [],
			value = null,
			handler = null,
			names = [],
			name = "",
			oldValue = null,
			handlers = handlers || {};
		for(var i = 0, len = elems.length; i < len; i++) {
			elem = elems[i];
			name = elem.getAttribute("name");
			if(name && names.indexOf(name) < 0) {
				names.push(name);
			}
		}
		
		for(var i = 0, len = names.length; i <  len; i++) {
			name = names[i];
			path = name;
			
			if(!path || path.charAt(0) !== "/") {
				continue;
			}
			//兼容IE6/7
			if(Y.env.ua.ie <= 7) {
				elem = D.query("[name='" + name + "']", form);
				if(elem.length === 1) {
					elem = elem[0];
				}
			} else {
				elem = form[name];
			}
			
			//要求被忽略的元素
			if(D.getAncestorByClassName(elem, "store-ignored")) continue;
			
			handler = handlers[path];
			//有些表单元素需要经过特殊的处理,才能获取到正确的值
			if(YAHOO.lang.isFunction(handler)) {
				value = handler.call(this, elem);
				this.put(path, value);
			} else if(!elem.tagName && elem.length){	//判断NodeList,处理多个radio同名的情况
				var elt = null;
				var checked = false;
				var isCheckOrRadio = false;
				for(var j = 0, jlen = elem.length; j < jlen; j++) {
					elt = elem[j];
					if(elt.type == "radio" || elt.type == "checkbox") {
						if(elt.checked) {
							value = elt.value;
							this.put(path, value);
							checked = true;
						}
						isCheckOrRadio = true;
					} 
					if(elt.type === "text" || elt.type === "hidden"){//页面上也有可能多个input重复
						if(!D.getAncestorByClassName(elt, "store-ignored")) {//未必忽略
							value = elt.value;
							this.put(path, value);
						}
					}
				}
				if(isCheckOrRadio && checked === false) {
					this.remove(path);
				}
			} else if(elem.type == "radio" || elem.type == "checkbox") {//处理checkbox和radio
				if(elem.checked) {
					value = elem.value;
					this.put(path, value);
				} else {
					this.remove(path);
				}
			} else { //处理input和select
				value = elem.value;
				this.put(path, value);
			}
		}
	},
	
	toString: function(){
		var data = this.cloneRoot();
		return JSON.stringify(data);
	},
	
	getRoot: function() {
		return this.root;
	},
	
	cloneRoot: function() {
		return clone(this.root);
	},
	
	clone: function() {
		return new PathStore(this.cloneRoot());
	}
};

PathStore.cloneObject = clone;

window.PathStore = PathStore;

})()
适应于前端表单元素和后端JAVA对象成员一一对应的情景,一般应用于复杂表单数据的传输。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值