
Question:
<h3>My Goal</h3>
I want to databind the items
property <a href="http://jsbin.com/laxaridawa/edit?html,console,output" rel="nofollow">shown in this jsBin</a> -- and have the rendered HTML text output match the value shown in the console.
<a href="http://jsbin.com/laxaridawa/edit?html,console,output" rel="nofollow">When I open this jsBin</a>, in the right pane labeled <em>output</em>, I expect to see the following in the second line of text:
Lorem,Ipsum,foo,1,bar,1,baz,0,qux,0
<h3>What I actually see...</h3>
Instead, I see:
Lorem,Ipsum,foo,0,bar,0,baz,0,qux,0
But if you click the button labeled <em>Show</em>, and check the console, you will see the following:
console.logLorem,Ipsum,foo,1,bar,1,baz,0,qux,0
The two don't match.
Question<blockquote>
How do I update/databind these variables so the output pane matches the console value?
</blockquote>(Please show a working jsBin if possible.)
<h3>Code</h3> http://jsbin.com/laxaridawa/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">
</head>
<body>
<dom-module id="x-element">
<template>
<button on-tap="_show">Show</button>
<div>{{selected}}</div>
<div>{{items}}</div>
</template>
<script>
(function(){
Polymer({
is: 'x-element',
properties: {
items: {
type: Array,
notify: true,
reflectToAttribute: true,
computed: '_computeItems(selected)',
value: function() {
return [['Lorem', 'Ipsum'], ['foo', 0], ['bar', 0], ['baz', 0], ['qux', 0],];
}
},
selected: {
type: Array,
notify: true,
reflectToAttribute: true,
},
},
_computeItems: function(a) {
var out = this.items,
selectedLength = a.length,
i = out.length;
while(i---1){
var j = selectedLength;
while(j--) {
if(a.indexOf(out[i][0])===-1){
out[i][1] = 0;
}
else if(a.indexOf(out[i][0])>-1){
out[i][1] = 1;
}
else {
console.log('Error: Undefined index of selected item');
}
}
}
return out;
},
ready: function(){
this.set('items', this._computeItems(this.seletcted));
},
_show: function() {
console.log('Selected: ' + this.selected);
console.log('Items: ' + this.items);
},
});
})();
</script>
</dom-module>
<x-element
selected='["foo","bar"]'
></x-element>
</body>
Answer1:You can't treat one property as computed <strong>and</strong> have a value. Only one of those at one time.
You don't need to invoke recalculation of a computed property. When the property listed in the arguments of the computed
function the computed property is recalculated automatically. In your case every time selected
changes.
As far as I've found out, the problem is caused by you editing the items
array in place.
It would seem, that after running the compute function _computeItems
, Polymer evaluates whether the value has changed by comparing references. Since you've reused the array, the reference didn't change and changes aren't propagated further in the framework (both to bindings and other computed properties, see
jsBin below).
I've found two ways to make it work:
<ol><li>make a copy of the array and return the copy</li> <li>manually call notifyPath to notify that the value has changed</li> </ol>Here's a <a href="http://jsbin.com/jaxisuhofe/edit?html,console,output" rel="nofollow">jsBin</a> with example fixes. Replace one of the three lines inside _computeItems
with it's commented version and it should work. I've also added a computed property that depends on items, to show that without the fix, that property isn't recalculated properly either.
Edit: I'd like to point out, that this seems to mean that it's perfectly correct to have a property that has <strong>both</strong> compute
and value
. Value is simply an initial value, which will immediatly be recalculated because selected
's value is set.
<strong>WARNING</strong>
If both items
and selected
have an initial value and items
is computed based on selected
, things get hairy - compute may be run before an initial value is set, depending on declaration order.
<a href="http://jsbin.com/kinugekoto/edit?html,console,output" rel="nofollow">jsBin</a> When items
is first, the order seems to be items-value, selected-value, items-compute and items-compute sees this.items
having the initial value.
<a href="http://jsbin.com/lelozozaxu/edit?html,console,output" rel="nofollow">jsBin</a> When selected
is declated first, then selected-value is ran first which triggers items-compute. Now, if items-compute returns undefined, items-value is taken, <strong>but if items-compute returns a value</strong>, items-value seems to be never used (check by uncommenting code in _computeItems
).