advanced data binding in Polymer - polymer

i have a little problem that i can not solve on my own.
I have custom element:
<dom-module id="station">
<template>
<country-iso-alpha3 id="country" selected={{country}}></country-iso-alpha3>
</template>
this custom element station has country property with CZE default value.
if we look in country-iso-alpha3 :
<paper-dropdown-menu>
<paper-menu class="dropdown-content" attr-for-selected="type" selected="{{selected}}" >
<paper-item type="CZE">
<span>CZE</span>
</paper-item>
<paper-item type="ENG">
<span>ENG</span>
</paper-item>
</paper-menu>
</paper-dropdown-menu>
properties of country-iso-alpha3 are:
properties: {
label: {},
selected: {},
},
what i am trying to achieve is that when user tap on paper item inside paper menu, property country in station element should update. But the only thing that is updated is selected attribute in station
Is there any way how to achieve this? Maybe this is already 3 way data binding.
I know, my english is not best and this is not so easy to demonstrate so if you do not undestand i can try to explain it a little bit better

In your country-iso-alpha3 element, configure selected to propagate changes upward. This can be done by setting notify: true.
properties: {
selected: {
type: String,
notify: true
}
},
By default, changes are not propagated to the parent element (docs).

Related

Refresh dom-repeat on a change of sort mode - no mutation needed to array set for sort

I have a dom-repeat template and I want to be able to sort the list real time. So, the way I went about it is the way below. However, I need a way to refresh that dom-repeat when a user selects a new selection from paper-menu.
Typically, a modification to the result set array would do it. But in this case, a change in sorting the list doesn't require adding or removing anything in that array set(therefore no array mutation).
How could I get <template is="dom-repeat" items="[[pic.results173]]" as="url" sort="_sortList"> to refresh?
<paper-menu-button>
<paper-button class="dropdown-trigger" raised>
<iron-icon icon="visibility"></iron-icon>
<span>View By</span>
</paper-button>
<paper-menu id="viewMode" class="dropdown-content" selected="0">
<paper-item>Most Recent</paper-item>
<paper-item>Least Recent</paper-item>
<paper-item>Highest Price</paper-item>
<template is="dom-repeat" items="[[pic.results173]]"
as="url" sort="_sortList">
_sortList: function(first, second) {
var el = this.$.viewMode;
var listMode;
switch(el.selected) {
case 0:
listMode = this._mostRecent(first , second);
break;
}
return listMode;
},
The developers guide has your answer. Give your template an id (say id="list") and call this.$.list.render()

Complete example of Polymer Two Way Binding

The polymer documentation has the following two way binding example:
<script>
Polymer({
is: 'custom-element',
properties: {
someProp: {
type: String,
notify: true
}
}
});
</script>
...
<!-- changes to "value" propagate downward to "someProp" on child -->
<!-- changes to "someProp" propagate upward to "value" on host -->
<custom-element some-prop="{{value}}"></custom-element>
I'm looking for a complete example that includes the design of the child, programmatic and interactive events the can cause upward and downward propagation of the `{{value}} property, and a demo of of the complete setup / example.
Here are some examples on js fiddle that demonstrate different ways of binding:
Two-way binding:
https://jsfiddle.net/tej70osf/
One-way binding: notify is not set on value property of the child element:
https://jsfiddle.net/tej70osf/1/
One-way binding: notify is set to true true on value property of the child element however the value property is bound using square brackets [[value]] instead of {{value}}:
https://jsfiddle.net/tej70osf/2/
Hope that helps
<dom-module id="user-demo">
<template>
<paper-input label="FIRST NAME" value="{{firstName}}"></paper-input>
</template>
</dom-module>
<user-demo></user-demo>
In your javascript code:
Polymer({
is: 'user-demo',
properties: {
firstName: {
type: String,
value: 'John',
notify: true
}
}
});
Check out the following fiddle for the full example:
https://jsfiddle.net/meenakshi_dhanani/6ffwh0qv/
I tried to use more polymer elements and two way binding. Hope it helps

How to properly bind iron-selector to iron-pages?

I am trying to do the same basic task as this other question, iron-selector selected={{}} binding with iron-pages selected={{}}, but I believe my case must be different, since I cannot replicate the solution.
I want to have a menu of buttons with iron-selector, and when clicked, the content with iron-pagesshould change.
So I have the actual page about.htm that has 1 webcomponent for the button-menu, called about-buttons.htm and then 1 webcomponent for each page, that should load according to which button is pushed by the user; about-who.htm, about-manifesto and about-team.
My question is:
How can I with this structure of my web components bind my buttons with my pages - and / or why is my current method wrong?
There's a lot of different ways to do a simple data binding like this. My method here is definitely not simple, and it does not work (by clicking buttons, the pages does not change).
So my about.htm looks like this (and this is the page people will visit):
<about-buttons selected="{{who}}">
</about-buttons>
<about-iron-pages attr-for-selected="name" selected="{{who}}" fallback-selection="who">
<about-us name="who">
</about-us>
<about-manifesto name="manifesto">
</about-manifesto>
<about-team name="team">
</about-team>
</about-iron-pages>
My about-buttons.htm looks like this:
<iron-selector
attr-for-selected="name"
selected="{{buttonSelected}}"
fallback-selection="who"
class="f-column f-column_3 f-column_mobile_2">
<button class="f-button-group__button" name="manifesto">Manifesto</button>
<button class="f-button-group__button" name="who">Who we are</button>
<button class="f-button-group__button" name="team">Team</button>
</iron-selector>
With this script:
<script>
Polymer({
is: 'about-buttons',
properties: {
buttonSelected: {
type: String,
notify: true,
value: 'who'
}
}
});
</script>
And here's my version of iron-pages:
<dom-module id="about-iron-pages">
<template>
<style>
:host {
display: block;
}
:host > ::content > :not(.iron-selected) {
display: none !important;
}
</style>
<content>
</content>
</template>
<script>
Polymer({
is: 'about-iron-pages',
behaviors: [
Polymer.IronResizableBehavior,
Polymer.IronSelectableBehavior
],
properties: {
activateEvent: {
type: String,
value: null,
}
},
observers: [
'_selectedPageChanged(selected)'
],
_selectedPageChanged: function(selected, old) {
this.async(this.notifyResize);
}
});
</script>
</dom-module>
As already pointed out, your attribute in <about-buttons selected="..."> does not match the actual property name. For <about-buttons>.buttonSelected, your attribute should be button-selected, and that's the only change to your code needed to get the selector working (plunker):
<about-buttons button-selected="{{who}}" ...>
Perhaps there's more context to your need for <about-buttons> and <about-iron-pages>, but otherwise, if you're only trying to implement a tabbed view, you could just use Polymer's components.
Specifically, <about-buttons> could be replaced by <paper-tabs> and <about-iron-pages> by <iron-pages> (plunker).

Polymer Iron list display is incorrect

<dom-module id="page-list">
<style include="shared-styles"></style>
<style>
iron-list {
height: 500px;
}
</style>
<template>
<neon-animated-pages id="pages" selected="0">
<neon-animatable><paper-button id="list-button" raised on-tap="_onOK" >List</paper-button></neon-animatable>
<neon-animatable>
<iron-list id="list" items="[[data]]" as="item">
<template>
<div>
Name: <span>[[item.name]]</span>
</div>
</template>
</iron-list>
</neon-animatable>
</neon-animated-pages>
</template>
<script>
Polymer({
is: "page-list",
attached: function () {
this.data=[
{
index: 0,
name: "Liz Grimes"
},
{
index: 1,
name: "Frazier Lara"
},
{
index: 2,
name: "Dora Griffith"
}];
this.$.list.fire('resize');
},
behaviors: [
Polymer.NeonAnimatableBehavior
],
properties: {
data: {
type: Array,
notify: true
}
},
_onOK: function (e) {
this.$.pages.selected="1";
}
});
</script>
After pushing the "List" button, the iron list is expected to be totatlly displayed. Unfortunately, the result is a display of one line with "Name:" and nothing else. If I force the selected page to "1", <neon-animated-pages id="pages" selected="1"> which means that on the first resfresh of the browser, the page with the iron list is displayed, the display is correct : three lines with the correct data.
The issue seems to be related to the fact that on startup, the page with the iron list is not yet displayed.
Anybody knows how to solve this ?
Seems to be a known issue https://github.com/PolymerElements/neon-animation/issues/115
Please check my suggested solution on github.
I think you need
this.set('data', [...]);
instead of
this.data=[...];
for Polymer to get notice of data change
The following solution is merely a hack, but if you listen to the iron-resize event on your neon-animatable and then call notifyResize() on any child iron-list elements you should be able to get around this issue until a more sustainable fix can be made to https://github.com/PolymerElements/neon-animation/issues/115

Tap listener for polymer iron-list item?

I have a custom element that utilizes iron-list to display an array of objects. Each item is generated via a template as follows:
<iron-list id="projectList" items="[[projects]]" indexAs="_id" as="projLI" class="layout flex">
<template>
<div>
<paper-material id="itemShadow" animated elevation="1">
<div class="item layout horizontal" onmouseover="hoverOver(this)" onmouseout="hoverOut(this)">
<!-- I use a paper-menu-button to display a list of available actions here -->
<!-- list item object content here such as: [[projLI.desc]] etc. -->
</div>
</paper-material>
</div>
</template>
</iron-list>
What is the best polymer-friendly approach to detect both a tap event on the iron-list item itself (ideally knowing which item was actually tapped via projLI._id), yet also be able to handle the internal paper-menu-button tap events in a different way?
I've eye-balled polymer 1.0's new event listeners (https://www.polymer-project.org/1.0/docs/devguide/events.html), as a possible approach, attempting to listen for different element tap events (as shown in example 1 on that page), but I'm not sure if that will work here. I've also considered possibly using iron-selector somehow around iron-list? Is that doable? I'm not sure that will work either, given that iron-selector would only have one child (i.e. the iron-list element and not it's templated children).
I feel like I'm missing a really easy way to accomplish this. Can someone please show me the light?
Follow the model outlined on lines 154 and 184 of this demo.
https://github.com/PolymerElements/iron-list/blob/master/demo/collapse.html
my-element.html
<iron-list items="[[items]]">
<template>
<my-list-item on-tap="_toggleMe"></my-list-item>
</template>
</iron-list>
...
_toggleMe: function(e) {
console.log(e.model.index);
}
The key is to place the event and listener method (toggleMe() in this case) inside the <template> of the iron-list. This allows the iron-list to register the array index.
I do this by encoding an array index in a list element id, then pulling the id out of a list item event target. Here is an example Polymer element that does this.
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-list/iron-list.html">
<dom-module id="list-example">
<style>
:host {
display: block;
}
#list-example {
height: 100px;
}
</style>
<template>
<paper-material animated elevation="1">
<iron-list id="list-example" items="[[data]]">
<template>
<div id="{{index2id(item.index)}}" on-mouseover="onMouseOverItem">{{item.name}}</div>
</template>
</iron-list>
</paper-material>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'list-example',
ready: function() {
for(var i = 0; i < this.data.length; i++) {
this.data[i].index = i;
}
},
index2id: function(index) {
return "_" + index;
},
id2index: function(id) {
return Number(id.substr(1));
},
onMouseOverItem: function(e) {
console.log('on-mouseover list item:', this.data[this.id2index(e.target.getAttribute('id'))]);
},
properties: {
data: {
type: Array,
value: [{name: 'A'}, {name: 'B'}, {name: 'C'},
{name: 'D'}, {name: 'E'}, {name: 'F'},
{name: 'G'}, {name: 'H'}, {name: 'I'}]
}
}
});
})();
</script>
I was having a similar issue and solved my problem using <array-selector> as follows:
<iron-list items="{{myarray}}" as="ref">
<template>
<div>
<paper-checkbox on-tap="toggleSelection"></paper-checkbox>
<span>{{ref.name}}</span>
</div>
</template>
</iron-list>
<array-selector id="arrsel" items="{{myarray}}"
selected="{{selectedName}}" toggle></array-selector>
and myarray is an array of objects:
var myarray = [{name: "Alice"}, {name: "Ben"}, ...]
and the function toggleSelection is defined as follows:
toggleSelection: function(e) {
console.log ("Selected index is " + e.model.index);
console.log ("Selected name is " + e.model.ref);
this.$.arrsel.select (e.model.ref);
console.log ("Current selection: ", this.selectedName);
}
The field name ref after e.model.__ is the value of the as attribute of iron-list.
WARNING: The variable e.model is not officially documented on the Polymer 1.0 iron-list doc (https://elements.polymer-project.org/elements/iron-list), however I discovered it during my debugging session. I am assuming that e.model is a public property (the coding style of Polymer uses underscore prefix for private property such as: _scroll_Position) and it is not a candidate for deprecation.
I just solved my issue of here https://groups.google.com/forum/#!topic/polymer-dev/r9IsUKVnLVM. Reading this documentation https://www.polymer-project.org/1.0/docs/devguide/events.html.
I hope it helps you!
Wrap your iron-list with an iron-selector - this allows you get to get the row selected / tapped.
(NB: you might need to remove your custom indexAs="_id" attribute to get the right row index)
<iron-selector attr-for-selected="index" on-tap="_itemSelected">
<iron-list id="projectList" items="[[projects]]" as="projLI" class="fit">
<template>
<div class="layout horizontal center" id="{{index}}">
<!-- your row content here -->
</div>
</template>
</iron-list>
</iron-selector>
Polymer method for row item selected:
_itemSelected: function (e) {
console.log(e.target.id); // selected iron-list row index
}