iron-list and spinner do not work together - polymer

I have an iron-list displaying my items:
<iron-list id="list" items="[[items]]" as="item" selection-enabled multi-selection>
<template>
...
</template>
</iron-list>
Everything works fine, but I get items from an external service, and it takes time, so I'd like to display a spinner until the data is received.
So I tried this:
<template is="dom-if" if="[[!items]]">
<paper-spinner-lite alt="Loading items list" active></paper-spinner-lite>
</template>
<template is="dom-if" if="[[items]]">
<iron-list id="list" items="[[items]]" as="item" selection-enabled multi-selection>
<template>
...
</template>
</iron-list>
</template>
but it does not work -- items are not displayed at all even though it is loaded from the backend service.
By experimenting, I found that items stopped displaying after I added the first <template is="dom-if" if="[[!items]]">. After I removed it (keeping the spinner and second template if), items started to be displayed when it loaded.
Where is the mistake?

items is an Array, and arrays (even empty ones) in Javascript are truthy, so your binding of [[!items]] would always evaluate to false.
You could use a computed binding to evaluate the length of items:
// template
<template is="dom-if" if="[[_isEmpty(items.*)]]">...</template>
<template is="dom-if" if="[[!_isEmpty(items.*)]]">...</template>
// script
Polymer({
...
_isEmpty: function() {
return this.items.length === 0;
}
);
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="iron-list/iron-list.html">
<link rel="import" href="paper-spinner/paper-spinner-lite.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<style>
iron-list {
height: 400px;
}
</style>
<template>
<template is="dom-if" if="[[_isEmpty(items.*)]]">
<paper-spinner-lite alt="Loading items list" active></paper-spinner-lite>
</template>
<template is="dom-if" if="[[!_isEmpty(items.*)]]">
<iron-list items="[[items]]">
<template>
<div>[[index]]: [[item]]</div>
</template>
</iron-list>
</template>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() {
return [];
}
}
},
_isEmpty: function() {
return this.items.length === 0;
},
_loadData: function() {
console.log('loading data...');
for (var i = 0; i < 100; i++) {
this.push('items', Math.random());
}
},
ready: function() {
this.async(this._loadData, 2000);
}
});
});
</script>
</dom-module>
</body>
codepen
Or you could declare a Boolean property, set to true by an observer based on items (which is basically a longer version of the solution above):
// template
<template is="dom-if" if="[[_isEmpty]]">...</template>
<template is="dom-if" if="[[!_isEmpty]]">...</template>
// script
Polymer({
...
properties: {
...
_isEmpty: {
type: Boolean,
value: true
}
},
observers: [
'_setEmpty(items.*)'
],
_setEmpty: function() {
this._isEmpty = this.items.length === 0;
}
});
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="iron-list/iron-list.html">
<link rel="import" href="paper-spinner/paper-spinner-lite.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<style>
iron-list {
height: 400px;
}
</style>
<template>
<template is="dom-if" if="[[_isEmpty]]">
<paper-spinner-lite alt="Loading items list" active></paper-spinner-lite>
</template>
<template is="dom-if" if="[[!_isEmpty]]">
<iron-list items="[[items]]">
<template>
<div>[[index]]: [[item]]</div>
</template>
</iron-list>
</template>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() {
return [];
}
},
_isEmpty: {
type: Boolean,
value: true
}
},
observers: [
'_setEmpty(items.*)'
],
_setEmpty: function() {
this._isEmpty = this.items.length === 0;
},
_loadData: function() {
console.log('loading data...');
for (var i = 0; i < 100; i++) {
this.push('items', Math.random());
}
},
ready: function() {
this.async(this._loadData, 2000);
}
});
});
</script>
</dom-module>
</body>
codepen

Related

iron-list selected-item observer returns null before sending selected object

I am trying to implement single selection on iron list. For every valid click, I get a null immediately followed by actual value.
Here is the iron-list code
<iron-list items="[[imagearray]]" as="item" id="itemlist" scroll-target="document" selected-item="{{selectedItem}}" selection-enabled grid>
<template>
<div class = "flexchild"style="width:50%">
<iron-image class = "imagecontainer" sizing = "cover" style = "width:calc(100% - 4px); height:180px;"
src=[[item.linkwebp]]></iron-image>
</div>
</template>
</iron-list>
Here is my property object
selectedItem:
{
type:Object,
observer: '_itemSelected'
}
_itemSelected()
{
console.log(this.selectedItem);
*
}
Each time i select an iron-list element, I get null followed by actual element. Any idea whY
Seams there is no error in your code. May cause something else. Below demo link works quite good.
DEMO
<html>
<head>
<base href="https://polygit.org/polymer+:master/components/">
<link rel="import" href="polymer/polymer.html" >
<link rel="import" href="iron-ajax/iron-ajax.html">
<link rel="import" href="iron-list/iron-list.html">
<link rel="import" href="iron-image/iron-image.html">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<style>
:host {
display: block;
height: 100%;
}
</style>
<!--In order to retrive some object data -->
<iron-ajax
auto
id="ajax"
url="https://randomuser.me/api?results=10"
last-response="{{response}}"> </iron-ajax>
<iron-list items="[[response.results]]" as="item" id="itemlist" scroll-target="document" selected-item="{{selectedItem}}" selection-enabled grid>
<template>
<div class = "flexchild" style="width:50%">
<iron-image style ="width: 40px;height:40px; border-radius:30px;" src='[[item.picture.large]]'></iron-image>
<span>[[item.name.first]] [[item.name.last]]</span>
</div><br/>
</template>
</iron-list>
<pre>[[obj]]</pre>
</template>
<script>
HTMLImports.whenReady(function() {
class XFoo extends Polymer.Element {
static get is() { return 'x-foo'; }
static get properties() { return {
selectedItem: {
type:Object,
observer: '_itemSelected'
}
}}
_itemSelected() {
this.obj = JSON.stringify(this.selectedItem, null, 4);
console.log(this.selectedItem);
}
}
customElements.define(XFoo.is, XFoo);
});
</script>
</dom-module>
</body>
</html>

Getting Child Element From Polymer.dom(event)

What is the best way to grab a hold of the element paper-ripple to attach a event listener for the animation end event? This element will be in a dom repeat. I tried getting children from Polymer.dom(event).localTarget; but had no success. The on-tap needs to stay in the parent.
<div on-tap="goToSingleListing" data-listingID="[[url.listing_id]]">
<paper-ripple></paper-ripple>
...
goToSingleListing: function(event) {
var el = Polymer.dom(event).localTarget;
var firstChildElementName = Polymer.dom(el).firstChild;
...
firstChildElementName.addEventListener('transitionend', ()=> {
page(url);
});
Instead of trying to get a reference to the <paper-ripple> from your tap event handler, you could use an annotated event listener directly on the <paper-ripple> for its transitionend event.
// dom-module template
<template is="dom-repeat" items="[[items]]">
<div on-tap="...">
<paper-ripple on-transitionend="_onItemTransitionEnd"></paper-ripple>
</div>
</template>
// dom-module script
Polymer({
...
_onItemTransitionEnd: function(e) {
...
}
}
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
_onItemTap: function(e) {
console.log('tap', e.model.index);
},
_onItemTransitionEnd: function(e) {
console.log('transitionend', e.model.index);
}
});
});
<head>
<base href="https://polygit.org/polymer+1.7.1/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="paper-ripple/paper-ripple.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<template is="dom-repeat" items="[1,2,3,4]">
<div on-tap="_onItemTap">
<h2>Item [[item]]</h2>
<paper-ripple on-transitionend="_onItemTransitionEnd"></paper-ripple>
</div>
</template>
</template>
</dom-module>
</body>
codepen

Polymer 1.x: Imperatively accessing DOM nodes inside a dom-if template

Here is my jsBin.
I want to access by its id a DOM node inside a dom-if template. With my existing code, I expect to see true when attempting to cast these nodes to Booleans, but instead I get false (i.e., undefined).
Steps to recreate the problem:
Open this jsBin.
Understand that the HTML pane on the right is working correctly by showing Foo and Bar and not showing Baz.
Observe the console and understand the id="foo" node is accessible but the id="bar" and id="baz" elements are not. And this is my problem.
How can I access those nodes imperatively?
http://jsbin.com/kemoravote/1/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">
<link href="iron-form/iron-form.html" rel="import">
</head>
<body>
<dom-module id="x-element">
<template>
<style></style>
<div id="foo">Foo</div>
<template is="dom-if" if="{{show}}">
<div id="bar">Bar</div>
</template>
<template is="dom-if" if="{{!show}}">
<div id="baz">Baz</div>
</template>
</template>
<script>
(function(){
Polymer({
is: "x-element",
properties: {
show: {
type: Boolean,
value: function() {
return true;
}
}
},
attached: function() {
console.log('foo', !!this.$.foo);
console.log('bar', !!this.$.bar);
console.log('baz', !!this.$.baz);
},
});
})();
</script>
</dom-module>
<x-element></x-element>
</body>
The $ selector won't work. You have to use $$ as follows:
console.log('baz', !!this.$$('#baz'));
Here is the jsBin.
http://jsbin.com/xogediyato/1/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">
<link href="iron-form/iron-form.html" rel="import">
</head>
<body>
<dom-module id="x-element">
<template>
<style></style>
<div id="foo">Foo</div>
<template is="dom-if" if="{{show}}">
<div id="bar">Bar</div>
</template>
<template is="dom-if" if="{{!show}}">
<div id="baz">Baz</div>
</template>
</template>
<script>
(function(){
Polymer({
is: "x-element",
properties: {
show: {
type: Boolean,
value: function() {
return true;
}
}
},
attached: function() {
console.log('foo', !!this.$.foo);
console.log('bar', !!this.$.bar);
console.log('baz', !!this.$.baz);
console.log('bar', !!this.$$('#bar'));
console.log('baz', !!this.$$('#baz'));
},
});
})();
</script>
</dom-module>
<x-element></x-element>
</body>

Initialize element when selected using iron-pages

I would like to initialize an element whenever it is rendered using iron pages.
In the jsbin do the below operation
Click 'Toggle'
Click 'Toggle step'
Click 'Toggle'
Click 'Toggle' again
I expect to see 'step a'. As the x-example assigns 'step="a"'. However I see 'step b'. Is there any way to show 'step a' whenever y-example is shown?
jsbin: http://jsbin.com/regapemoqe/1/edit?html,output
Code:
<meta charset="utf-8">
<base href="http://polymer-magic-server.appspot.com/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<link href="iron-pages/iron-pages.html" rel="import">
<style>
body {
font-family: sans-serif;
}
</style>
</head>
<body>
<x-example></x-example>
<dom-module id="x-example">
<style>
:host {
display: block;
}
</style>
<template>
<button on-click="toggleSelection">Toggle</button>
<iron-pages
attr-for-selected="data-route"
selected=[[selected]]>
<section data-route="one">
<p>This is the one</p>
</section>
<section data-route="two">
<p>This is the other one</p>
<y-example
step="a"></y-example>
</section>
</iron-pages>
</template>
<script>
// only need this when both (1) in the main document and (2) on non-Chrome browsers
addEventListener('WebComponentsReady', function() {
Polymer({
is: "x-example",
properties: {
selected: {
type: String,
value: 'one'
}
},
toggleSelection: function() {
this.selected = (this.selected == 'one')?'two':'one';
}
});
});
</script>
</dom-module>
<dom-module id="y-example">
<style>
:host {
display: block;
}
</style>
<template>
<iron-pages
attr-for-selected="data-step"
selected="[[step]]">
<section data-step="a"><p>This is step a</p></section>
<section data-step="b"><p>This is step b</p></section>
</iron-pages>
<button on-click="toggleStep">Toggle step</button>
</template>
<script>
addEventListener('WebComponentsReady', function() {
Polymer({
is: "y-example",
properties: {
step: {
type: String,
value: 'a'
}
},
toggleStep: function() {
this.step = (this.step == 'a')?'b':'a'
}
});
});
</script>
</dom-module>
</body>
You could do something like this in your toggleSelection function of your x-example element:
toggleSelection: function() {
this.$$('y-example').step='a';
this.selected = (this.selected == 'one')?'two':'one';
}
See here: http://jsbin.com/tewaqa/edit?html,output

Polymer 1.0 Child element not firing to parent

I'm having trouble trying to fire an event from a child element to its parent on Polymer 1.0, and I can't see what am I doing wrong.
EDITED: Add more code
Child's code:
<link rel="import" href="../components/polymer/polymer.html" />
<link rel="import" href="../components/iron-input/iron-input.html" />
<link rel="import" href="../components/iron-icon/iron-icon.html" />
<link rel="import" href="../components/paper-input/paper-input container.html" />
<link rel="import" href="../components/paper-input/paper-input-error.html" />
<link rel="import" href="../components/iron-input/iron-input.html" />
<link rel="import" href="custom-table.html" />
<dom-module id="master-admin">
<style is="custom-style">
[...]
</style>
<template>
<custom-table selectable collist="{{columns}}" data="{{data}}">
</custom-table>
<div id="newrow" class="horizontal layout" hidden>
<template is="dom-repeat" items="{{columns}}" as="col">
<paper-input-container class="container flex">
<label class="label">{{col.name}}</label>
<input class="input" id="input" is="iron-input">
</paper-input-container>
</template>
</div>
<div id="iconsdiv" class="horizontal layout">
<iron-icon id="adicon" icon="add-circle" on-click="addrow"></iron-icon>
<div class="flex"></div>
<iron-icon id="delicon" icon="cancel" on-click="delrow"></iron-icon>
<iron-icon id="edicon" icon="create" on-click="editrow"></iron-icon>
</div>
</template>
<script>
Polymer({
is: 'master-admin',
properties: {
doctype: String,
columns: Array,
data: Array
},
datachanged: function () {
debugger;
var updatedata = {
data: this.data,
doctype: this.doctype
};
this.fire('data-updated', updatedata);
},
addrow: function (e) {
[...]
this.datachanged();
},
delrow: function (e) {
[...]
this.datachanged();
},
editrow: function (e) {
[...]
this.datachanged();
}
});
</script>
</dom-module>
On parent:
<master-admin
doctype="{{master.DocumentalTypeId}}"
columns="{{master.Columns}}"
data="{{master.Data}}"
on-data-updated="masterupdated">
</master-admin>
masterupdated: function () {
alert('updated master!');
}
Just to be clear, datachanged works just fine and it's called when it should. masterupdated on the parent does not.
The alert is never firing, nor the code gives any error. I guess is just something with Polymer 1.0 that works different.
The basic setup you describe does work, there must be a problem in the details somewhere. Here is an example:
<!doctype html>
<head>
<meta charset="utf-8">
<base href="http://milestech.net/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<style>
body {
font-family: sans-serif;
}
</style>
</head>
<body>
<x-parent></x-parent>
<hr>
<dom-module id="master-admin">
<template>
<button on-tap="updateData">Update Data</button>
</template>
<script>
// only need this when both (1) in the main document and (2) on non-Chrome browsers
addEventListener('WebComponentsReady', function() {
Polymer({
is: 'master-admin',
updateData: function() {
this.fire('data-updated');
}
});
});
</script>
</dom-module>
<dom-module id="x-parent">
<style>
</style>
<template>
<master-admin on-data-updated="masterupdated"></master-admin>
</template>
<script>
// only need this when both (1) in the main document and (2) on non-Chrome browsers
addEventListener('WebComponentsReady', function() {
Polymer({
is: 'x-parent',
masterupdated: function() {
document.body.appendChild(this.create('h3', {textContent: 'data-updated!'}));
}
});
});
</script>
</dom-module>
</body>
Can you try this?
<template is="dom-bind" id="t">
<master-admin
doctype="{{master.DocumentalTypeId}}"
columns="{{master.Columns}}"
data="{{master.Data}}"
on-data-updated="masterupdated">
</master-admin>
</template>
<script>
var template = document.querySelector('#t');
template.masterupdated= function () {
consloe.log("MASTER UPDATED!!");
};
</script>