Polymer 1.x:观察员

Mow*_*zer 0 javascript observers polymer polymer-1.0

最终,我想从这个geochart中选择个别状态.但是这个问题仅限于让观察者被标记_computeData为响应于改变selected状态数组而被激活.

通过以下步骤重现问题:

  1. 打开这个jsBin.
  2. 清除控制台.
  3. 选择德克萨斯州.

请注意控制台显示:

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

这是预期的这条线:

console.log('You selected: ' + this.selected); // Logs properly
Run Code Online (Sandbox Code Playgroud)

但是,我希望控制台也能读到:

按此行:

_computeData: function() {
  console.log('selected'); // Does not log properly; function not called?
  ...
Run Code Online (Sandbox Code Playgroud)

应该由以下一组观察员调用.

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

这里发生了什么?观察者为什么不调用该_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>
Run Code Online (Sandbox Code Playgroud)

Sco*_*les 6

你的问题在这里:

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

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

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

解决方案有两个方面:

1)在对数组进行排序的情况下,为setvia 创建一个新的数组引用slice():

if (index === -1) {
    selected.push(string);
    selected.sort();
    this.set('selected', selected.slice());
Run Code Online (Sandbox Code Playgroud)

2)在您只是拼接的情况下,使用splice辅助函数:

} else {
    this.splice('selected', index, 1);
}
Run Code Online (Sandbox Code Playgroud)

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

注意:_computeData正在调用这些更改,但现在它的调用次数太多了.这部分是由于观测selected.*,这将火selected,selected.lengthselected.splices.观察selected.length而不是selected.*可能有所帮助.

UPDATE

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

  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在您的应用程序上触发乘法事件).我会欣赏它,如果你将提交Buggoogle-chart,并链接回本左右.:)

这是一个修改过的版本,我修补了猴子google-chart.drawChart,解决了其他两个主要问题,并进行了各种小修.

<!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>
Run Code Online (Sandbox Code Playgroud)

HTH


随机额外的东西:

var _this = this;
setTimeout(function() {
    _this._drawChart();
}.bind(_this), 100)
Run Code Online (Sandbox Code Playgroud)

您需要任何捕捉的值this(_this)使用bind,但它没有任何意义一举两得.

setTimeout(function() {
    this._drawChart();
}.bind(this), 100)
Run Code Online (Sandbox Code Playgroud)

... 足够.

  • 由于相同的原因`set('selected',selected)`不起作用. (2认同)