如何在 Polymer 1.0 中对对象而不是数组使用 dom-repeat?

How to use dom-repeat with objects instead of arrays in Polymer 1.0?

遍历数组 myarray=[1, 2, 3] 的工作方式如下:

<template is="dom-repeat" items="[[myarray]]">
    <span>[[item]]</span>
</template>

如何遍历对象 myobject = {a:1, b:2, c:3}

您需要将此对象转换为有意义的数组,以便能够使用 dom-repeat 对其进行迭代。

我用初始值创建了一个 myObj 属性。然后我创建了一个名为 myObjAsArray 的 属性,它是一个空数组。在 local dom 准备就绪时调用的 ready 回调函数中,我遍历 myObj 的所有属性并添加它们到 myObjAsArray(请参阅 here 以了解如何遍历对象属性)。然后,您可以使用 dom-repeat.

遍历此数组
<link rel="import" href="bower_components/polymer/polymer.html">

<dom-module id="test-element">
    <style>
    </style>
    <template>
        <template is="dom-repeat" items="{{myObjAsArray}}">
            name: <span>{{item.name}}</span>
            value: <span>{{item.value}}</span>
        </template>
    </template>
</dom-module>

<script>
    Polymer({
        is: "test-element",
        properties: {
            myObj: {
                type: Object,
                value: function () {
                    return {
                        a: 1,
                        b: 2,
                        c: 3
                    };
                }
            },
            myObjAsArray: {
                type: Array,
                value: function () {
                    return [];
                }
            }
        },
        attached: function () {
            var propArray = [];
            for (var prop in this.myObj) {
                if (this.myObj.hasOwnProperty(prop)) {
                    propArray.push({name: prop, value: this.myObj[prop]});
                }
            }

            this.myObjAsArray = propArray;
        }
    });
</script>

我一直在使用Object.keys(obj).map(function(prop){return {id:prop, val:obj[prop]}})

这是一个完整的实现:

<test-element obj='{"a": 1, "b": 2, "c": 3}'></test-element>

<dom-module id="test-element">
    <template>

        <template is="dom-repeat" items="{{_toArray(obj)}}">
            name: <span>{{item.name}}</span>
            <br> value: <span>{{item.value}}</span>
            <br>
            <hr>
        </template>

    </template>
    <script>
    Polymer({

        properties: {
            obj: Object
        },

        _toArray: function(obj) {
            return Object.keys(obj).map(function(key) {
                return {
                    name: key,
                    value: obj[key]
                };
            });
        }

    });
    </script>

</dom-module>

我遇到了同样的问题,但我的用例要求更高:我需要通过重复进行双向深度绑定。另外,我负担不起在每次更改时重写整棵树。

由于我没有找到解决办法,而且polymer团队在这个问题上似乎处理得比较慢,所以我暂时做了一些东西。它是用 ES2015 编写的,但将其转换为 vanilla ES5 应该很简单。无论如何按原样在 Chrome 中运行。或将其扔给婴儿。 This page 详细说明如何。这篇文章的要点:

vulcanize element.html --inline-script --inline-css | \
    crisper -h element.v.html -j element.js;
babel element.js -o element.js

我们开始吧:

<link rel="import" href="../../bower_components/polymer/polymer.html">

<dom-module id="my-objarray">
    <script>
(function() {
    'use strict';

    class Objarray {
        beforeRegister() {
            this.is = 'my-objarray';
            this.properties = {
                array:{
                    notify:true,
                    type:Array,
                    value:function() {return new Array();}
                },
                object:{
                    notify:true,
                    type:Object
                }
            };
            this.observers = ['_onArray(array.*)', '_onObject(object.*)'];
        }
        _onObject(change) {
            if(this._setting) return;
            if(change.path == "object") this._rewriteArray();
            else this._writeElement(change);
        }

        _rewriteArray() {
            this.splice("array", 0, this.array.length);
            for(let i in this.object) {
                this.push("array", {key:i, value:this.object[i]});
            }
        }

        _writeElement(change) {
            const path = change.path.match(/^object\.([^\.]+)(.*)$/);
            const key = path[1];
            const objectPath = "object." + key + (path[2] || "");
            const id = this._getId(key);
            const arrayPath = "array." + id + ".value" + (path[2] || "");
            this.set(arrayPath, this.get(objectPath));
        }

        _getId(key) {
            const collection = Polymer.Collection.get(this.array);
            for(const element of this.array) {
                if((element && element.key) === key) {
                    return collection.getKey(element);
                }
            }
        }

        _onArray(change) {
            let path = change.path.match(/^array\.(#\d+)\.([^\.]+)(\.|$)/);
            if(!path) return;
            let id = path[1], field = path[2];
            if(field == "key") throw new Error("my-objarray: Must not change key!");
            if(field != "value") throw new Error("my-objarray: Only change inside value!");
            this._setting = true;
            this.set(this._getPath(change, id), change.value);
            delete this._setting;
        }

        _getPath(change, id) {
            let collection = Polymer.Collection.get(change.base);
            let index = change.base.indexOf(collection.getItem(id));
            let key = change.base[index].key;
            return change.path.replace("array." + id + ".value", "object." + key);
        }

    }

    Polymer(Objarray);
})();
    </script>
</dom-module>

用法:

<dom-module id="my-objarray-test">
    <template strip-whitespace>
        <my-objarray object="{{items}}" array="{{array}}"></my-objarray>
        <template is="dom-repeat" items="{{array}}">
            <div>
                <label>{{item.key}}:</label>
                <input type="number" value="{{item.value.data::input}}">
            </div>
        </template>
    </template>
    <script>
(function() {
    'use strict';

    class ObjarrayTest {
        beforeRegister() {
            this.is = 'my-repeat-test';
            this.properties = {
                items:{
                    notify:true,
                    type:Object,
                    value:function() {return new Object();}
                }
            };
            this.observers = ['_onItems(items.*)'];
        }

        ready() {
            console.log("my-repeat-test.ready");
            this.items = {a:{data:1}, b:{data:2}};
        }

        _onItems(change) {console.log("test._onItems", change.path);}

    }

    Polymer(ObjarrayTest);
})();
    </script>
</dom-module>

希望对某人有所帮助。 Presumable polymer 现在获得像明天一样的功能:-)

Object.keys() 似乎在 IE 中不起作用。因此修改了实现以使用 _.map 代替。

<test-element obj='{"a": 1, "b": 2, "c": 3}'></test-element>

<dom-module id="test-element">
  <template>

    <template is="dom-repeat" items="{{getKeyValue(obj)}}">
      key: <span>{{item.key}}</span>
      <br> value: <span>{{item.value}}</span>
      <br>
      <hr>
    </template>

  </template>
  <script>
    Polymer({

      properties: {
        obj: Object
      },

      getKeyValue: function(obj) {
        return _.map(obj, function(value, key) {
          return {
            key: key,
            value: value
          };
        });
      }

    });

  </script>

</dom-module>

https://jsfiddle.net/avidlearner/36jnb16d/

重新审视此问题以解决其他人提到的问题。这与所有浏览器兼容并使用 hasOwnProperty.

<template is="dom-repeat" items="[[_toArray(obj)]]">
  key: [[item.key]] val: [[item.val]]
</template>

...

_toArray: function(obj, deep) {
  var array = [];
  for (var key in obj) {
    if (deep || obj.hasOwnProperty(key)) {
      array.push({
        key: key,
        val: obj[key]
      });
    }
  }
  return array;
}