Polymer 1.x:观察员

时间:2016-02-12 12:44:15

标签: javascript polymer polymer-1.0 observers

最终,我想选择个别州from this geochart。但是这个问题仅限于让标记为_computeData的观察者在响应变异selected状态数组时触发。

使用以下步骤重现问题:

  1. Open this jsBin
  2. 清除控制台。
  3. 选择德克萨斯州。
  4. 请注意控制台显示:

      

    您选择的是:科罗拉多州,南达科他州,德克萨斯州

    预计按此行:

    console.log('You selected: ' + this.selected); // Logs properly
    

    但是,我希望控制台 读取:

      

    选择

    按此行:

    _computeData: function() {
      console.log('selected'); // Does not log properly; function not called?
      ...
    

    应由以下一组观察者调用。

    http://jsbin.com/wuqugigeha/1/edit?html,console,output
    ...
    observers: [
      '_computeData(items.*, selected.*)',
      '_dataChanged(data.*)',
    ],
    ...
    

    问题

    这里发生了什么?为什么观察者没有调用_computeData方法?在改变selected数组后,可以采取哪些措施来解决方法?

    http://jsbin.com/wuqugigeha/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]]"
            xon-google-chart-select="_onGoogleChartSelect">
          </google-chart>
        </template>
        <script>
          (function() {
            Polymer({
              is: 'x-element',
              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, // '#455A64'
                  value: function() {
                    return 'blue';
                  }
                },
                options: {
                  type: Object,
                  notify: true,
                  reflectToAttribute: true,
                  computed: '_computeOptions(color)',
                },
                selected: {
                  type: Array,
                  notify: true,
                  reflectToAttribute: true,
                  value: function() {
                    return [];
                  },
                  //observer: '_computeData', // Unsuccessfully tried this
                },
                data: {
                  type: Array,
                  notify: true,
                  reflectToAttribute: true,
                  //computed: '_computeData(items.*, selected.*)', // Unsuccessfully tried this
                },
              },
              observers: [
                '_computeData(items.*, selected.*)',
                '_dataChanged(data.*)',
              ],
              // Bind select event listener to chart
              ready: function() {
                var _this = this;
                this.$.geochart.addEventListener('google-chart-select', function(e) {
                  this._onGoogleChartSelect(e);
                }.bind(_this));
              },
              _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) {
                  selected.push(string);
                  selected.sort();
                } else {
                  selected.splice(index, 1);
                }
                this.set('selected', selected);
                console.log('You selected: ' + this.selected); // Logs properly
                // Next step should be '_computeData' per observers
              },
              // After 'items' populates or 'selected' changes, compute 'data'
              _computeData: function() {
                console.log('selected'); // Does not log properly; function not called?
                var data = [],
                    items = this.items,
                    selected = this.selected,
                    i = items.length;
                while (i--) {
                  data.unshift([items[i], selected.indexOf(items[i]) > -1 ? 1 : 0]);
                }
                data.unshift(['State', 'Select']);
                this.set('data', data);
              },
              // After 'data' changes, redraw chart
              // Add delay to avoid 'google not defined' error
              _dataChanged: function() {
                var _this = this;
                setTimeout(function() {
                  _this._drawChart();
                }.bind(_this), 100)
              },
              // After delay, draw chart
              _drawChart: function() {
                var data = this.data,
                    dataTable = this.$.geochart._createDataTable(data);
                console.log(dataTable);
                this.$.geochart._chartObject.draw(dataTable, this.options);
              },
              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>
    

2 个答案:

答案 0 :(得分:6)

你的问题在这里:

if (index === -1) {
    selected.push(string);
    selected.sort();
} else {
    selected.splice(index, 1);
}
this.set('selected', selected);

Polymer的数据处理方法(如set)允许您为Polymer提供有关数据更改方式的具体信息,从而允许Polymer进行非常快速的DOM更新。

在这种情况下,你正在做Polymer无法看到它的工作(即数组操作),然后让set弄清楚发生了什么。但是,当您调用this.set('selected', selected);时,Polymer看到selected的标识未发生更改(即,它与之前的Array对象相同),它只是停止处理。 (Fwiw,这是一个常见的问题,因此我们正在考虑进行修改,无论如何都要检查阵列。)

解决方案有两个方面:

1)如果您要对数组进行排序,请通过setslice()创建一个全新的数组引用:

if (index === -1) {
    selected.push(string);
    selected.sort();
    this.set('selected', selected.slice());

2)如果您只是拼接,请使用splice辅助函数:

} else {
    this.splice('selected', index, 1);
}

理想情况下,您要避免对数组进行排序,然后直接使用this.push

注意:正在调用这些更改_computeData,但现在它的调用次数太多了。部分原因是观察selected.*selectedselected.lengthselected.splices。观察selected.length代替selected.*可能有所帮助。

更新

您的示例还有其他三个主要问题:

  1. data绑定到google-chart(即data="[[data]]"),因此当data更改时图表会自动重绘,我们可以完全删除_drawChart
  2. _computeData(items.*, selected.*)过于激进,因为selected.*将触发'selected.length','selected.splices'和selected的更改。而是使用_computeData(items, selected.length)
  3. google-chart本身似乎是错误的。特别是,它自己的drawChart未设置为正确可重入。最明显的问题是,每次图表绘制时,它都会添加一个额外的选择侦听器(这会导致在您的应用程序中触发多个chart-select事件)。如果您google-chart google-chart.drawChart并{}返回此SO,我会欣赏。 :)
  4. 这是一个修改过的版本,我修补了<!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() { // monkey-patching google-chart 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', 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, // '#455A64' value: 'blue' }, options: { type: Object, computed: '_computeOptions(color)', }, selected: { type: Array, value: function() { return []; } }, data: { type: Array, computed: '_computeData(items, 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) { console.log('_onGoogleChartSelect: ', e.detail) 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); } // Next step should be '_computeData' per observers console.log('_select:', this.selected); }, // After 'items' populates or 'selected' changes, compute 'data' _computeData: function(items, selectedInfo) { console.log('_computeData'); 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>,解决了其他两个主要问题,并做了各种小修理。

    var _this = this;
    setTimeout(function() {
        _this._drawChart();
    }.bind(_this), 100)
    

    HTH

    随机额外的东西:

    this

    您需要 捕获_thisbind使用setTimeout(function() { this._drawChart(); }.bind(this), 100) 的值,但它不会做两件事都有意义。

    =AND(F5<>"",F5<G5,F5<H5)

    ......就够了。

答案 1 :(得分:0)

Here is an example of implementation of the accepted solution

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>