Polymer 1.x:数据绑定数组
Polymer 1.x: Databinding Arrays
Objective
我想将 selected
属性 in this element 数据绑定到 DOM(在 <div>[[selected]]</div>
)。
选择状态后(如 "Texas"),我希望状态名称出现在 "Colorado,South Dakota" 预选状态旁边的左上角(在 <div>[[selected]]</div>
标签)。但事实并非如此。
问题
这是怎么回事?我怎样才能成功地将 DOM 数据绑定到 selected
属性?并让 DOM 随着每个新状态的选择和取消选择自动更新。
重现问题
按照以下步骤操作:
- Open this jsBin.
- 观察预选的州是科罗拉多州和南达科他州。
- 请注意,左上角列出了 Colarado 和南达科他州。
- 点击德克萨斯州。
- 观察图表中选中的德克萨斯州。
- ❌ 请注意德克萨斯州 未包含在左上角的选定州列表中。即,
<div>[[selected]]</div>
- 单击标记为 "Show Values" 的按钮并在控制台中观察 "Texas" 包含在
selected
数组中。
尝试使用 .slice() 的解决方案崩溃
。
@ScottMiles 写道:
...you are doing work where Polymer cannot see it (i.e. the array manipulations), and then asking set
to figure out what happened. However, when you call this.set('selected', selected);
, Polymer sees that the identity of selected
hasn't changed (that is, it's the same Array object as before) and it simply stops processing.
所以我尝试通过在原始数组上使用 .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>
如果你想绑定数组你必须使用dom-repeat。
<template is="dom-repeat" items="[[selected]]">[[item]] </template>
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(', ');
},
数据绑定数组可能很棘手。
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>
Objective
我想将 selected
属性 in this element 数据绑定到 DOM(在 <div>[[selected]]</div>
)。
选择状态后(如 "Texas"),我希望状态名称出现在 "Colorado,South Dakota" 预选状态旁边的左上角(在 <div>[[selected]]</div>
标签)。但事实并非如此。
问题
这是怎么回事?我怎样才能成功地将 DOM 数据绑定到 selected
属性?并让 DOM 随着每个新状态的选择和取消选择自动更新。
重现问题
按照以下步骤操作:
- Open this jsBin.
- 观察预选的州是科罗拉多州和南达科他州。
- 请注意,左上角列出了 Colarado 和南达科他州。
- 点击德克萨斯州。
- 观察图表中选中的德克萨斯州。
- ❌ 请注意德克萨斯州 未包含在左上角的选定州列表中。即,
<div>[[selected]]</div>
- 单击标记为 "Show Values" 的按钮并在控制台中观察 "Texas" 包含在
selected
数组中。
尝试使用 .slice() 的解决方案崩溃
...you are doing work where Polymer cannot see it (i.e. the array manipulations), and then asking
set
to figure out what happened. However, when you callthis.set('selected', selected);
, Polymer sees that the identity ofselected
hasn't changed (that is, it's the same Array object as before) and it simply stops processing.
所以我尝试通过在原始数组上使用 .slice()
方法创建一个新数组来解决问题,如下所示。
// 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>
如果你想绑定数组你必须使用dom-repeat。
<template is="dom-repeat" items="[[selected]]">[[item]] </template>
dom-repeat
的格式不支持正确使用逗号分隔符。所以我改用计算的 属性 字符串。
<div>[[selectedString]]</div>
...
selectedString: {
type: String,
computed: '_computeSelectedString(selected.length)',
},
...
_computeSelectedString: function(selectedInfo) {
return this.selected.sort().join(', ');
},
数据绑定数组可能很棘手。
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>