聚合物 1.x:数据绑定阵列

Polymer 1.x: Databinding Arrays

本文关键字:数据绑定 阵列 聚合物      更新时间:2023-09-26

Objective

我想将此元素中的 selected 属性数据绑定到 DOM(在 <div>[[selected]]</div> 处(。

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

问题

这是怎么回事?如何成功地将 DOM 数据绑定到 selected 属性?并让 DOM 自动更新,其中包含选择和取消选择的每个新状态。

重现问题

请按照以下步骤操作:

  1. 打开这个 jsBin。
  2. 观察预先选择的州是科罗拉多州和南达科他州。
  3. 注意 科拉
  4. 拉多和南达科他州列在左上角。
  5. 单击德克萨斯州。
  6. 观察图表中选中的德克萨斯州。
  7. ❌ 请注意,德克萨斯州不包括在左上角的选定州列表中。 即<div>[[selected]]</div>
  8. 单击标有"显示值"的按钮,然后在控制台中观察"德克萨斯州"包含在selected数组中。

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

这个SO答案解释了为什么聚合物对象不能直接观察到突变阵列。

@ScottMiles写道:

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

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

http://jsbin.com/yosajuwime/1/edit?html,console,output
// 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,console,output
<!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>

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

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

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

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

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

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

所以这里有一个完整的实现示例。

http://jsbin.com/xonanucela/edit?html,console,output
<!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>