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));
View Code

 

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>
View Code

 

后续会逐步更新 ... ...

posted @ 2017-08-18 10:11  【云】风过无痕  阅读(265)  评论(0编辑  收藏  举报