Reputation: 16102
I want to databind the selected
property in this element to the DOM (at <div>[[selected]]</div>
).
After selecting a state (like "Texas"), I expect the state's name to appear in the upper left next to the pre-selected states of "Colorado,South Dakota" (inside the <div>[[selected]]</div>
tag). But instead, it does not.
What's going on here? How can I successfully databind the DOM to the selected
property? And have the DOM update automatically with each new state that is selected and de-selected.
Follow these steps:
<div>[[selected]]</div>
selected
array.This SO answer explains why mutated arrays are not directly observable by the Polymer object.
@ScottMiles writes:...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.
So I attempted to solve the problem by creating a new array using the .slice()
method on the original array as follows.
// Sort new 'selected' array
_selectedChanged: function(selectedInfo) {
var selected = this.selected.slice();
selected.sort();
//this.set('selected', selected); // Uncommenting this line crashes call stack
},
This seems to solve the observability problem but creates a side effect issue and crashes the call stack.
<!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>
Upvotes: 0
Views: 472
Reputation: 16102
Databinding arrays can be tricky.
So here is a complete implementation example.
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>
Upvotes: 0
Reputation: 2929
If you want to bind array you have to use the dom-repeat.
<template is="dom-repeat" items="[[selected]]">[[item]] </template>
http://jsbin.com/soxedeqemo/edit?html,console,output
Upvotes: 1
Reputation: 16102
The formatting of dom-repeat
doesn't support proper use of comma delimiters. So I used a computed property string instead.
<div>[[selectedString]]</div>
...
selectedString: {
type: String,
computed: '_computeSelectedString(selected.length)',
},
...
_computeSelectedString: function(selectedInfo) {
return this.selected.sort().join(', ');
},
Upvotes: 0