最终,我想选择个别州from this geochart。但是这个问题仅限于让标记为_computeData
的观察者在响应变异selected
状态数组时触发。
使用以下步骤重现问题:
请注意控制台显示:
您选择的是:科罗拉多州,南达科他州,德克萨斯州
预计按此行:
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
数组后,可以采取哪些措施来解决方法?
<!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>
答案 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)如果您要对数组进行排序,请通过set
为slice()
创建一个全新的数组引用:
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.*
,selected
,selected.length
和selected.splices
。观察selected.length
代替selected.*
可能有所帮助。
您的示例还有其他三个主要问题:
data
绑定到google-chart
(即data="[[data]]"
),因此当data
更改时图表会自动重绘,我们可以完全删除_drawChart
。 _computeData(items.*, selected.*)
过于激进,因为selected.*
将触发'selected.length','selected.splices'和selected
的更改。而是使用_computeData(items, selected.length)
。google-chart
本身似乎是错误的。特别是,它自己的drawChart
未设置为正确可重入。最明显的问题是,每次图表绘制时,它都会添加一个额外的选择侦听器(这会导致在您的应用程序中触发多个chart-select
事件)。如果您google-chart
google-chart.drawChart
并{}返回此SO,我会欣赏。 :)这是一个修改过的版本,我修补了<!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
您需要 捕获_this
(bind
)或使用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>