Polymer 1.0 - data-bound arrays not updating correctly - polymer

Demo here: http://jsbin.com/wiqowo/15/edit?html,output
When I loop through this.data in the example below, it iterates over the correct number of items, however the value of the individual items is not output. Is this a bug, or am I missing something?
<dom-module id="test-element">
<template>
<h3>data</h3>
<p>data: <span>{{data}}</span></p>
<ul>
<template is="dom-repeat" items="{{data}}">
<li>{{item}}</li>
</template>
</ul>
</template>
<script>
Polymer({
ready: function() {
this.data = ['Item #1', 'Item #2', 'Item #3'];
for (var i=this.data.length; i<10; i++) {
this.data.push('Item #' + (i+1));
}
console.log(this.data);
}
});
</script>
</dom-module>

Figured it out. Apparently array mutation is not supported in 1.0. You have to call the new Polymer.push, Polymer.pop, etc. methods.
Demo: http://jsbin.com/wiqowo/25/edit?html,output
for (var i=this.data.length; i<10; i++) {
this.push('data', 'Item #' + (i+1));
}

Define an array var inside of the the ready function, and push data to it. Then, assign it to this.data.
Polymer({
ready: function() {
var localData = ['Item #1', 'Item #2', 'Item #3'];
for (var i=localData.length; i<10; i++) {
localData.push('Item #' + (i+1));
}
this.data = localData;
console.log(this.data);
}
});

Related

Passing attribute to custom element in Polymer as a function

I'm trying to do something really simple in Polymer. I'm trying to pass an attribute using a function.
<dom-module id="my-fixtures">
<template>
<fixtures-list fromdate="[[_requiredDay(24)]]"></fixtures-list>
</template>
<script>
Polymer({
is : 'my-fixtures',
properties: {
fromdate: String
},
_requiredDay: function (offset) {
// 1 day before now
var d = new Date(new Date().getTime() - parseInt(offset) * 60 * 60 * 1000);
var n = d.toJSON();
return n;
}
});
</script>
</dom-module>
But it' not working. If I change the function for a static srting value it works. Any help?
Thanks
The fromdate property should be on fixtures-list not on my-fixtures
https://jsbin.com/jucevujeyo/edit?html,console,output

How to access and modify dom-repeat children dynamically

Using Polymer 1.0, suppose I have some elements nested in a dom-repeat element, such as the following:
<div id="stuffDiv">
<template is="dom-repeat" items="{{myItems}}">
<iron-icon icon="star" on-tap="onTap"></iron-icon>
</template>
</div>
I'd like to change an attribute, say class, on all the iron-icons within the "onTap" method. So I've figured out the following implementation
onTap: function(e) {
var index = e.model.index;
var stuffIcons = Polymer.dom(this.$.stuffDiv).querySelectorAll('iron-icon');
for (var i = 0; i <= index; ++i) {
this.toggleClass('magicClass', true, stuffIcons[i]);
}
for (var i = index+1; i < stuffIcons.length; ++i) {
this.toggleClass('magicClass', false, stuffIcons[i]);
}
},
This works, but feels very clunky. Is there a better approach that's not well documented? I don't see anything obvious on Polymer 1.0's resources.
Note, i've also tried having iron-icon's class depend on an item value and dynamically update that in onTap using this.set(...), however that doesn't work either, the class values I set get obliterated for reasons unknown to me.
You could look at binding the magicClass class.
Is this the result you were after?
<div id="stuffDiv">
<template is="dom-repeat" items="{{myItems}}">
<iron-icon icon="star" on-tap="onTap" class$="{{getIconClass(index, stopTheMagicIndex)}}">Star</iron-icon>
</template>
</div>
And then...
properties: {
myItems: {
type: Array,
value: function() {
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
}
},
stopTheMagicIndex: Number
},
onTap: function(e) {
this.set('stopTheMagicIndex', e.model.index);
},
getIconClass: function(index) {
if (index <= this.stopTheMagicIndex) return 'magicClass';
return '';
}

Polymer 1.0 template bind ref equivalent

In Polymer 0.5, you could do:
<template bind ref="howdyTpl"></template>
...
<template id="howdyTpl">Howdy {{whatever}}</template>
And the template with id 'howdyTpl' would get stamped where referenced in the ref attribute.
How can I do something similar in Polymer 1.0?
Here's a custom element that does something similar.
Polymer({
is: 'bind-ref',
properties: {
ref: {
type: String,
observer: 'refChanged',
},
},
refChanged: function(newRef, oldRef) {
if (oldRef != undefined) {
this._removeChildren();
this._stamp();
}
},
_stamp: function() {
this._parent = Polymer.dom(this).parentNode;
var rootEl = this._parent;
while (Polymer.dom(rootEl).parentNode) {
rootEl = Polymer.dom(rootEl).parentNode;
}
var tpl = Polymer.dom(rootEl).querySelector('#' + this.ref);
var tplRoot = tpl.stamp().root;
this._children = Array.prototype.slice.call(tplRoot.childNodes);
Polymer.dom(this._parent).insertBefore(tplRoot, this);
},
_removeChildren: function() {
for (var i = 0; i < this._children.length; i++) {
Polymer.dom(this._parent).removeChild(this._children[i]);
}
},
attached: function() { this._stamp(); },
detached: function() { this._removeChildren(); },
});
The target template element must be a dom-template element.
<bind-ref ref="howdyTpl"></bind-ref>
<template is="dom-template" id="howdyTpl">Howdy <span>{{whatever}}</span></template>
(Code snippets licensed Apache 2.0.)
I've created an element that resolves this problem. It's kind of a hack, but it works... including with multi-depth file inclusion and data binding that the other answer provided did not handle.

Polymer: changing the array and template using JQuery

I've established an array, like this:
<template>
<ul id="iconContainer" style="list-style: none; padding: 0; margin: 0;">
<template repeat="{{icon in icons}}">
<li class="flex icon-container"><app-icon label="{{icon.label}}" src="{{icon.src}}"></app-icon></li>
</template>
</ul>
</template>
<script>
Polymer('app-grid', {
ready: function() {
this.icons = [];
for(i = 1; i < 21; i++) {
this.icons.push({label: 'Item ' + i, src: '*'});
},
iconChanged: function() {
this.$.iconContainer.getElementsByTagName('template')[0].iterator_.updateIteratedValue();
}
});
</script>
And I'm getting frustrated on trying to find out how to alter this array from jquery on index.html as shown below to update the template.
<app-grid></app-grid>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).querySelector('app-grid').icons.push({label: 'foo', src: '*'});
</script>
This doesn't seem to work though.
Thoughts?
I'm not sure why you need jquery for this. The document.querySelector('app-grid') works just fine to get the element.
Before poking around at element properties, the element needs to have upgraded. The polymer-ready event is your signal that all elements are upgraded:
document.addEventListener('polymer-ready', function(e) {
document.querySelector('app-grid').icons.push({label: 'foo', src: '*'});
});
Demo: http://jsbin.com/wasafomesuba/1/edit

How To Access Polymer Custom Element From Callback

I need to access the custom element and call its method from the click event callback.
<polymer-element name="my-element">
<template>
<style type="text/css" media="screen">
...
</style>
<ul id="my_data"></ul>
</template>
<script>
Polymer('my-element', {
dataSelected: function(selectedText) {
//...
},
setData: function(data) {
for (var i = 0; i < data.length; i++) {
var li = document.createElement('li');
li.addEventListener('click', function(e) {
// how can I call dataSelected() from here?
});
li.innerText = data[i];
this.$.my_data.appendChild(li);
}
}
});
</script>
</polymer-element>
How can I call the custom element's dataSelected() method from the callback?
You can use bind to attach a this context to any function, so:
li.addEventListener('click', function(e) {
this.dataSelected(e.target.innerText);
}.bind(this));
http://jsbin.com/xorex/4/edit
But you can make things easier by using more Polymer sugaring. For example, you can publish data and use the observation system, like so:
<polymer-element name="my-element" attributes="data">
...
data: [], // type hint that data is an array
...
dataChanged: function() { // formerly setData
http://jsbin.com/xorex/5/edit
Also, you can use the built-in event system instead of addEventListener
<polymer-element name="my-element" attributes="data">
...
<ul id="my_data" on-tap="{{dataTap}}"></ul>
...
dataTap: function(e) { // `tap` supports touch and mouse
if (e.target.localName === 'li') {
this.dataSelected(e.target.textContent);
}
}
http://jsbin.com/xorex/6/edit
But the biggest win is using <template repeat> instead of creating elements in JavaScript. At that point, the complete element can look like this:
<polymer-element name="my-element" attributes="data">
<template>
<ul id="my_data">
<template repeat="{{item in data}}">
<li on-tap="{{dataTap}}">{{item}}</li>
</template>
</ul>
</template>
<script>
Polymer('my-element', {
data: [],
dataTap: function(e) {
console.log('dataSelected: ' + e.target.textContent);
}
});
</script>
</polymer-element>
http://jsbin.com/xorex/7/edit
You could insert element = this; at the beginning of your setData() function and call element.dataSelected(); in the event handler.
But i think for what you want to achieve, you'd better use a repeat template (Iterative templates) and a direct binding to your click handler function (Declarative event mapping).