【问题标题】:Polymer 1.x: Databinding ArraysPolymer 1.x:数据绑定数组
【发布时间】:2016-02-21 07:55:22
【问题描述】:

目标

我想将selected 属性in this element 数据绑定到DOM(<div>[[selected]]</div>)。

选择一个州(如“德克萨斯”)后,我希望该州的名称出现在左上角的“科罗拉多州,南达科他州”的预选州旁边(在<div>[[selected]]</div> 标签内)。但相反,它没有。

问题

这里发生了什么?如何成功地将 DOM 数据绑定到 selected 属性?并且随着每个被选中和取消选中的新状态自动更新 DOM。

重现问题

按照以下步骤操作:

  1. Open this jsBin
  2. 注意预选的州是科罗拉多州和南达科他州。
  3. 注意科拉拉多和南达科他州列在左上角。
  4. 点击德克萨斯。
  5. 观察图表中选择了德克萨斯州。
  6. ❌ 请注意,德克萨斯州不包括左上角的选定州列表中。即<div>[[selected]]</div>
  7. 单击标有“显示值”的按钮并在控制台中观察到“Texas”包含在selected 数组中。

尝试使用 .slice() 的解决方案崩溃

This SO answer explains why mutated arrays are not directly observable by the Polymer object

@ScottMiles 写道:

...您正在从事 Polymer 看不到的工作(即数组操作),然后要求 set 弄清楚发生了什么。但是,当您调用 this.set('selected', selected); 时,Polymer 会看到 selected 的身份没有改变(也就是说,它与之前的 Array 对象相同)并且它只是停止处理。

所以我尝试通过在原始数组上使用.slice() 方法创建一个新数组来解决问题,如下所示。

http://jsbin.com/yosajuwime/1/edit?html,控制台,输出
// Sort new 'selected' array
_selectedChanged: function(selectedInfo) {
  var selected = this.selected.slice();
  selected.sort();
  //this.set('selected', selected); // Uncommenting this line crashes call stack
},

这似乎解决了可观察性问题,但会产生副作用问题并使调用堆栈崩溃。

来源

http://jsbin.com/yosajuwime/1/edit?html,控制台,输出
<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <base href="https://polygit.org/components/">
  <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link href="polymer/polymer.html" rel="import">
  <link href="google-chart/google-chart.html" rel="import"> </head>

<body>
  <dom-module id="x-element"> <template>
      <style>
        google-chart {
          width: 100%;
        }
      </style>
    <br><br><br><br>
    <button on-tap="_show">Show Values</button>
    <button on-tap="clearAll">Clear All</button>
    <button on-tap="selectAll">Select All</button>
    <div>[[selected]]</div>
    <google-chart id="geochart"
                  type="geo"
                  options="[[options]]"
                  data="[[data]]"
                  on-google-chart-select="_onGoogleChartSelect">
    </google-chart>
    </template>
    <script>
      (function() {

        // google-chart monkey patch
        var gcp = Object.getPrototypeOf(document.createElement('google-chart'));
        gcp.drawChart = function() {
          if (this._canDraw) {
            if (!this.options) {
              this.options = {};
            }
            if (!this._chartObject) {
              var chartClass = this._chartTypes[this.type];
              if (chartClass) {
                this._chartObject = new chartClass(this.$.chartdiv);
                google.visualization.events.addOneTimeListener(this._chartObject,
                    'ready', function() {
                        this.fire('google-chart-render');
                    }.bind(this));

                google.visualization.events.addListener(this._chartObject,
                    'select', function() {
                        this.selection = this._chartObject.getSelection();
                        this.fire('google-chart-select', { selection: this.selection });
                    }.bind(this));
                if (this._chartObject.setSelection){
                  this._chartObject.setSelection(this.selection);
                }
              }
            }
            if (this._chartObject) {
              this._chartObject.draw(this._dataTable, this.options);
            } else {
              this.$.chartdiv.innerHTML = 'Undefined chart type';
            }
          }
        };

        Polymer({
          is: 'x-element',
          /** /
           * Fired when user selects chart item.
           *
           * @event us-map-select
           * @param {object} detail Alpabetized array of selected state names.
          /**/
          properties: {
            items: {
              type: Array,
              value: function() {
                return [ 'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming', ].sort();
              },
            },
            color: {
              type: String,
              value: 'blue',
            },
            options: {
              type: Object,
              computed: '_computeOptions(color)',
            },
            selected: {
              type: Array,
              value: function() {
                return [];
              }
            },
            data: {
              type: Array,
              computed: '_computeData(items, selected.length)'
            },
          },
          observers: [
            '_selectedChanged(selected.length)',
          ],
          _computeOptions: function() {
            return {
              region: 'US',
              displayMode: 'regions',
              resolution: 'provinces',
              legend: 'none',
              defaultColor: 'white',
              colorAxis: {
                colors: ['#E0E0E0', this.color],
                minValue: 0,  
                maxValue: 1,
              }
            }
          },    
          // On select event, compute 'selected'
          _onGoogleChartSelect: function(e) {
            var string = e.path[0].textContent.split('Select')[0].trim(), // e.g. 'Ohio'
                selected = this.selected, // Array of selected items
                index = selected.indexOf(string);
            // If 'string' is not in 'selected' array, add it; else delete it
            if (index === -1) {
              this.push('selected', string);
            } else {
              this.splice('selected', index, 1);
            }
          },
          // Sort new 'selected' array
          _selectedChanged: function(selectedInfo) {
            var selected = this.selected.slice();
            selected.sort();
            //this.set('selected', selected); // Uncommenting this line crashes call stack
          },
          // After 'items' populates or 'selected' changes, compute 'data'
          _computeData: function(items, selectedInfo) {
            var data = [],
                selected = this.selected,
                i = items.length;
            while (i--) {
              data.unshift([items[i], selected.indexOf(items[i]) > -1 ? 1 : 0]);
            }
            data.unshift(['State', 'Select']);
            return data;
          },
          clearAll: function() {
            this.set('selected', []);
          },
          selectAll: function() {
            this.set('selected', this.items);
          },
          _show: function() {
            //console.log('items', this.items);
            console.log('selected', this.selected);
            //console.log('data', this.data);
          },
        });
      })();
    </script>
  </dom-module>
  <x-element color="red" selected='["Colorado", "South Dakota"]'></x-element>
</body>

</html>

【问题讨论】:

  • 您的 set craster calls call stack,因为您有效地说:“当所选数组更改时,更改选定的数组”,这是一个无限循环。 Fwiw,您的绑定以计算已排序的连接字符串的解决方案是一个很好的解决方案,因为您的输出相对简单。
  • I wrote this demo 查看外部元素是否存在任何问题,通过数据绑定观察数组属性的更改,而无需采取任何其他步骤。显然,当观察者是外部元素时,一切正常......plnkr.co/edit/NBsxZLkfg5fxFEXW06H9?p=preview

标签: javascript data-binding polymer polymer-1.0


【解决方案1】:

如果你想绑定数组,你必须使用 dom-repeat。

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

http://jsbin.com/soxedeqemo/edit?html,console,output

【讨论】:

【解决方案2】:

dom-repeat 的格式不支持正确使用逗号分隔符。所以我改用了计算属性字符串。

http://jsbin.com/ruduwavako/1/edit?html,控制台,输出
<div>[[selectedString]]</div>
...
selectedString: {
  type: String,
  computed: '_computeSelectedString(selected.length)',
},
...
_computeSelectedString: function(selectedInfo) {
  return this.selected.sort().join(', ');
},

【讨论】:

  • 为什么?您可以根据需要格式化 dom-repeat。但是如果它解决了问题就可以了..
  • 我认为@mowzer 是说如果你重复&lt;span&gt;{{name}},&lt;/span&gt;,你最终会得到一个逗号。确实可以通过额外的逻辑来解决这个问题,但是对于这种超简单的输出,计算字符串是一种有效的解决方案。
  • I wrote this demo 查看外部元素是否存在任何问题,通过数据绑定观察数组属性的更改,而无需采取任何额外的步骤。显然,当观察者是外部元素时,一切正常...plnkr.co/edit/NBsxZLkfg5fxFEXW06H9?p=preview
【解决方案3】:

数据绑定数组可能很棘手。

So here is a complete implementation example.

http://jsbin.com/xonanucela/edit?html,控制台,输出
<!doctype html>
<head>
  <meta charset="utf-8">
  <base href="https://polygit.org/components/">
  <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link href="paper-button/paper-button.html" rel="import">
</head>
<body>

<x-element></x-element>

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

  <br><br>
  <paper-button on-tap="_addNew">Click To Add</paper-button>
  <p>
    <strong>Items</strong>:
    <template is="dom-repeat" items="{{items}}">
      <span>[[item]] </span>
    </template>
  </p>

</template>
<script>
  Polymer({
    is: 'x-element',
    properties: {
      items: {
        type: Array,
        value: function() {
          return ['foo'];
        }
      }
    },
    _addNew: function() {
      var a = this.items; // Clones array
      a.push('bar'); // Updates "value"
      console.log('a', a);
      this.set('items', a.slice()); // Updates "identity"
      console.log('items', this.items);
    },

  });
</script>
</dom-module>
</body>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-26
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多