Object.defineProperty(obj,prop,descriptor)使用
初步实现了数据自动映射到html中,动态修改对象数据也很自动更新到html。提供addProps方法-添加新增属性并初始化自动监听
代码如下:
1、abserve.js:包含数据监听实现、类似jquery的find函数--querySelector实现等(数据深度迭代版)
(function(window) { var methods = { htmlToData: function(ctner) { // html反向映射到data var pName = this.name, vName = ctner ? ctner.getAttribute('varibleName') : null; if (vName) {} else { for (let key in window) { if (typeof window[key] == 'object') { try { var rObj = methods.getPropByPath(window[key], pName); if (rObj && rObj.v != undefined) { vName = key; if (ctner) ctner.setAttribute('varibleName', key); break; } } catch (e) {} } } } var pPath = vName + '.' + pName + '=\'' + this.value + '\''; console.log(pPath); eval(pPath); }, parseHtml: function({ key, value, container, fn }) { var inputs = methods.querySelector("[name=" + key + "]", container); inputs.forEach(function(item, index, array) { try { /* callback 提供设值接口 */ fn.apply(item, [value]); } catch (e) { item.value = value; } $(item).off("blur.render"); $(item).on("blur.render", function() { methods.htmlToData.apply(this, [container]); }); }); }, /*查找子节点,用法类似jquery的find函数,仅支持id,class,attr选择器,返回匹配的元素集*/ querySelector: function find(selector, el) { var el = el || document; var m = selector.match(/([#\.\[])([\w\W]+)/i); var type, key, attrName, result = []; if (m) { /* 正则判断选择器类型 */ if (m[1] == ".") { type = "class"; key = m[2]; } else if (m[1] == "#") { type = "id"; key = m[2]; } if (m[1] == "[") { type = "attr"; m = m[2].match(/(\w+)=((\w+(\[\d+\])*\.*)+)/i); attrName = m[1]; key = m[2]; } } else { type = "tag"; key = selector; } function findChild(node) { var c; for (var i = 0; i < node.childNodes.length; i++) { c = node.childNodes[i]; if (type == "class" && c.className == key) result.push(c); else if (type == "id" && c.id == key) { result.push(c); continue; } else if (type == "attr" && c.getAttribute && c.getAttribute( attrName) == key) { result.push(c); continue; } else if (type == "tag" && c.tagName && c.tagName.toLowerCase() == key) { result.push(c); continue; } findChild(c); } } findChild(el); return result; }, getPropByPath: function(obj, path) { let tempObj = obj; path = path.replace(/\[(\w+)\]/g, '.$1'); path = path.replace(/^\./, ''); let keyArr = path.split('.'); let i = 0; for (let len = keyArr.length; i < len - 1; ++i) { let key = keyArr[i]; if (key in tempObj) tempObj = tempObj[key]; else { throw new Error( '[render warn]: please transfer a valid prop path to form item!' ); } } return { o: tempObj, k: keyArr[i], v: tempObj[keyArr[i]] }; }, _copyProps: function(obj, target) { for (var key in target) obj[key] = target[key]; }, _defProps: function(obj, ctx, key, value, props, path, bool) { /* prop参数是为了解决二次赋值操作导致key值改变,parseHtml无法获得name名 */ (function(key, prop, props) { Object.defineProperty(obj, key, { set: function(newValue) { if (bool) methods._addProps(obj[key], newValue, ctx, path); else key = props[prop] = newValue; }, get: function() { return props[prop]; } }); })(key, key, props); /* 实现obj.prop 和 obj.data.prop双向更新 */ (function(ctx, key, value, props, path) { Object.defineProperty(props, key, { set: function(newValue) { if (key == newValue) return; key = newValue; path = path.replace(/\.*(undefined)*\.+/, '.') .replace(/\.(\d+)\./g, '[+$1+].').replace( /[+]/g, ''); var propPath = path.slice(path.indexOf('.') + 1); if (propPath.indexOf('data.') == 0) propPath = propPath.replace('data.', ''); methods.parseHtml({ key: propPath, value: newValue, container: document.querySelector(ctx), fn: Vue.prototype.setValue }); }, get: function() { return key; } }); props[key] = value; })(ctx, key, value, props, path); }, _addProps: function(obj, props, ctx, path) { for (var key in props) { if (typeof props[key] == 'object') { methods._addProps(obj[key] = {}, props[key], ctx, path + '.' + key); methods._defProps(obj, ctx, key, props[key], props, path + '.' + key, true); } else if (typeof props[key] == 'function') obj[key] = props[ key]; else methods._defProps(obj, ctx, key, props[key], props, path + '.' + key); } return obj; }, /* obj:目标对象;props:json格式属性;path:属性路径;el:渲染域容器 */ addProps: function(obj, props, path) { var target = obj; if (path) target = methods.getPropByPath(obj, path).v; methods._addProps(target, props, obj.el || 'document', '.' + path); } }; _ = { addProps: methods.addProps, getPropByPath: methods.getPropByPath, querySelector: methods.querySelector, parseHtml: methods.parseHtml, htmlToData: methods.htmlToData }; Vue = function(param) { methods._copyProps(this, param); if (this.data) methods._addProps(this, this.data, this.el); } Vue.prototype = { addProps: function(props, path) { methods.addProps(this, props, path); } }; }(window));
2、html代码:(实现对easyui组件的设值,其他组件也可以通过设值接口实现对应设值操作)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>javascript test</title> <link rel="stylesheet" type="text/css" href="css/metro/easyui.css"> <link rel="stylesheet" type="text/css" href="css/icon.css"> <link rel="stylesheet" type="text/css" href="css/demo.css"> <script type="text/javascript" src="js/jquery.min.js"></script> <script type="text/javascript" src="js/jquery.easyui.min.js"></script> </head> <body> <div class="easyui-panel" title="数据驱动dom更新" style="width:500px"> <div style="padding:10px 60px 20px 60px"> <h1>F12 console 中输入:a.user.name = 'jack'; <br> 可以看到界面也会更新数据,其他更新参考脚本。</h1> <form id="ff" class="easyui-form" method="post" data-options="novalidate:true" action="save.do"> <table cellpadding="5"> <tr> <td>对象的名称:</td> <td><input type="text" name="user.name" class="easyui-textbox" ></input></td> </tr> <tr> <td>对象的年龄:</td> <td><input name="user.age" class="easyui-numberbox"></td> </tr> <tr> <td>对象的描述:</td> <td> <input name="user.addr" class="easyui-textbox" data-options="height:60,multiline:true"> </td> </tr> <tr> <td>纯变量数量:</td> <td><input name="count" class="easyui-numberspinner"></td> </tr> <tr> <td>纯变量选项:</td> <td> <select class="easyui-combobox" name="dept" style="width:150px;"> <option value="1">aitem1</option> <option value="2">bitem2</option> <option value="3">bitem3</option> <option value="4">ditem4</option> <option value="5">eitem5</option> <option value="6">eitem6</option> <option value="7">eitem7</option> <option value="8">eitem8</option> <option value="9">eitem9</option> </select> </td> </tr> <tr> <td>menber-0-name:</td> <td><input name="menbers[0].id.code" class="easyui-textbox"></td> </tr> <tr> <td>menber-0-age:</td> <td> <input name="menbers[0].age" class="easyui-numberbox"> </td> </tr> <tr> <td>menber-1-name:</td> <td><input name="menbers[1].name" class="easyui-textbox"></td> </tr> <tr> <td>menber-1-age:</td> <td> <input name="menbers[1].childs[0].name" class="easyui-textbox"> </td> </tr> <tr> <td>danomick:</td> <td> <input name="menbers[0].addrs" class="easyui-textbox"> </td> </tr> <tr> <td>danomick user:</td> <td> <input name="user.email" class="easyui-textbox"> </td> </tr> <tr> <td>danomick a:</td> <td> <input name="phones" class="easyui-numberbox"> </td> </tr> </table> </form> </div> </div> <script type="text/javascript" src="render.js"></script> <script> "use strict" /* 扩展对easyui组件设值支持 */ Vue.prototype.setValue = function (value) { var $ctn = $(this).parent().prev(), ctnClass = $ctn.attr("class"), itemFn = ctnClass.match(/easyui-(\w+)/),itemFn = itemFn[1]; $ctn[itemFn]('setValue',value); }; /* 组件值反射到data中机制构建 */ $.parser.onComplete = function(context){ try { $('[type=hidden].textbox-value').each(function(){ var prev = $(this).prev(), $ctn = $(this).parent().prev(), ctnClass = $ctn.attr("class"), itemFn = ctnClass.match(/easyui-(\w+)/),itemFn = itemFn[1]; if (prev) { prev.off("blur.prev"); prev.on("blur.prev", function() { var item = $(this).next()[0]; setTimeout(function(){ item.value = $ctn[itemFn]('getValue'); _.htmlToData.apply(item); },150); }); } }); } catch (e) {} } /* 模仿Vue */ var a = new Vue({ el: '#ff', /* 指定扫描的容器 */ data:{ /* 要处理的数据 */ user:{ name: 'json', age: 26, addr: 'usa' }, menbers:[], count: 10, dept: 6 } }); /* 覆盖原有属性会自动建立映射关系 */ a.menbers = [ { mName: 'jack', age: 26, id: { code: 'idCard' } }, { name: 'timy', age: 20, childs:[ {name: 'kimi'}, {name: 'coco'} ] } ]; /* 针对没有的属性提供属性添加API(API会建立映射关系) */ a.addProps({email:'czm@139.com'},'user'); a.addProps({addrs:'lin ken street'},'data.menbers[0]'); a.addProps({phones:13900139000}); $("#scriptContent").text(JSON.stringify(a.data)); /* 模拟数据更新 */ // var int = setInterval(function () { // a.user.age = Math.round(Math.random()*100); // a.data.user.addr = new Date(); // a.count = Math.round(Math.random()*10000); // a.data.dept = Math.round(Math.random()*8 + 1); // },1500); </script> </body> </html>
后续会逐步更新 ... ...