Object.defineProperty(obj,prop,descriptor)使用

初步實現了數據自動映射到html中,動態修改對象數據也很自動更新到html。提供addProps方法-添加新增屬性並初始化自動監聽
代碼以下:

一、abserve.js:包含數據監聽實現、相似jquery的find函數--querySelector實現等(數據深度迭代版)javascript

(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

 

二、html代碼:(實現對easyui組件的設值,其餘組件也能夠經過設值接口實現對應設值操做)css

<!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

 

後續會逐步更新 ... ...html

相關文章
相關標籤/搜索