How to include dynamic properties in path name inside dom-repeat? - polymer

I am trying to bind the state of a checkbox to a specific nested subproperty of hostProperty. The checkboxes are generated inside a nested "dom-repeat" template (iterating through innerObj per outerObj), and the desired subproperty of hostProperty is relative to subproperties of the other objects, i.e. it is of the form hostProperty[outerObj.name][innerObj.name].
I have been unable to figure out the correct path name for the subproperty.
I have tried the following and all have failed:
- hostProperty.outerObj.name.innerObj.name (likely looks for hostProperty.outerObj.name which is undefined)
- ['hostProperty', outerObj.name, innerObj.name] (based on the documentation for specifying paths)
- hostProperty[outerObj.name][innerObj.name]
- hostProperty.outerObj['name'].innerObj['name']
<template is="dom-repeat" items="[[outerObj_arr]]" as="outerObj">
<tr>
<th scope="row">[[outerObj.name]]</th>
<template is="dom-repeat" items="[[innerObj_arr]]" as="innerObj">
<!-- broken path name below -->
<td><paper-checkbox checked="[[hostProperty.outerObj.name.innerObj.name]]"></paper-checkbox></td>
</template>
</tr>
</template>

What you are trying to do is impossible from the html template.
To get this working, you need to use a helper method, eg:
<td><paper-checkbox checked="[[_getChecked(hostProperty, outerObj.name, innerObj.name)]]"></paper-checkbox></td>
And then in js:
_getChecked(list, key1, key2) {
return list[key1][key2];
}

Related

Polymer 1.0 dom-repeat with nested elements

I have a question regarding dom-repeat. I have two different elements like:
<host-element>
<item-element></item-element>
<item-element></item-element>
</host-element>
each item-element has an array, in which items can be added at runtime. When the item-element
is attached is fires an event, so that the host-element knows about the item-element within its content and adds each
item-element to an array of item-elements to a property. To access the item-element
item arrays you could bind to the property of the host-element like:
<host-element items="{{itemElements}}">
<item-element></item-element>
<item-element></item-element>
</host-element>
to print the content of the itemElements iterate over it with dom-repeat
<template is="dom-repeat" items="{{itemElements}}">
<ul>
<template is="dom-repeat" items="{{item.values}}" as="value">
<li>[[value]]</li>
</template>
<ul>
</template>
so far everything works as expected. When the item-element change the dom-repeat
should redraw itself, but it is not happening. The documentation states you could uses dom-repeat.observe or dom-repeat.render
to update the dom-repeat element. Using dom-repeat.render manually works and could be
run automatically, but is not ideal. Therefor I am trying to find a solution with dom-repeat.observe with no luck so far.
<template is="dom-repeat" items="{{itemElements}}" observe="values.splice">
<ul>
<template is="dom-repeat" items="{{item.values}}" as="value" observe="????">
<li>[[value]]</li>
</template>
<ul>
</template>
I have pushed my source to github at source and a live demo
Thanks for your help.
Sandro
I have found a hack to get it to work. I need to alter the array holding all items.
The function _itemElementChanged is called every time a item-element is changed.
_itemElementChanged: function(){
// the check is needed if this function is run multiple times in the same tick it would erase the whole array
if (this.items.length > 0){
var itemsTmp = this.items;
this.items = [];
this.async(function () {
this.self.items = this.items;
}.bind({items: itemsTmp, self: this}));
}
}
The check for of this.items.length > 0 is need incase _itemElementChanged is called twice
before the async function runs. In that case this.items would end up empty.
This is by fare not a satisfying solution, but its the only working on I found so far.. I have updated the source to include the solution.

Get multiple values of selected paper-radio-button

I have a dom-repeat element that creates a paper-radio-group with a couple of paper-radio-buttons. These radio-buttons each contains two pieces of unique information loaded from an array. Model name and price. I need to be able to load the model name and price of the selected radio-button.
I'm able to load the name of the selected model but I can't figure out how to do it with the price at the same time. Here is the code I currently have that displays the name:
<paper-radio-group class="layout vertical" id="modelgrp" selected="{{selected}}" >
<template is="dom-repeat" items="{{models}}" >
<paper-radio-button name="{{item.model}}" id="modelsel"><span>{{item.model}}</span> <div class="paper-font-caption"><span>{{item.price}}</span> SEK</div></paper-radio-button>
</template>
</paper-radio-group>
<paper-item><span>{{selected}}</span></paper-item>
I need to be able to call the item.price of the selected item just like I call the item.model by writing {{selected}}.
I was sent this link as it is sort of answering my question but I don't really understand the code and how to apply it to mine.
Use selected-item and add a conditional attribute for each value (model and price) onto the paper-radio-button element:
<paper-radio-group class="layout vertical" selected-item="[[selectedItem]]">
<template is="dom-repeat" items="[[models]]" >
<paper-radio-button name="{{item.model}}" model$="[[item.model]]" price$="[[item.price]]"><span>[[item.model]]</span> <div class="paper-font-caption"><span>[[item.price]]</span> SEK</div></paper-radio-button>
</template>
</paper-radio-group>
<paper-item><span>[[model]]</span></paper-item>
<paper-item><span>[[price]]</span></paper-item>
Then setup an observer to monitor changes to selectedItem and set the two values you want to capture as follows:
...
observers: [ '_selectedItemChanged(selectedItem)' ],
_selectedItemChanged: function(el) {
this.price = el.getAttribute('price');
this.model = el.getAttribute('model');
},
...

Data binding between published properties of two custom elements inside an auto binding template - Polymer 1.0

Problem: I have an auto binding template in my main index.html page. Inside the template I am using two of my custom elements. One element is the producer of some data and the other one is the consumer of that data. These custom elements expose published/declared properties for each other to use and bind to. I was able to do that in Polymer 0.5 fairly easily (an example shown below). How do I do the same in Polymer 1.0?
How I used to do in Polymer 0.5?
In Polymer 0.5 I used to data bind between published properties of two custom elements using curly brace syntax and then inside it used the auto node finding concept to directly bind to other element's published property. An example shown below,
<template is="auto-binding">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
I tried something similar in Polymer 1.0 as shown in the example below
<template is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
But it throws an error:-
Uncaught TypeError: Cannot read property '$' of undefined
You can't do $.* bindings inside the template in Polymer 1.0. Instead, either refactor or use computed functions.
In your situation, since selectedLabel and toolbartitle shares the same value, it is much better to simply bind them to the same property.
Also, attribute names that are declaratively passed in (through the element tag) need to be serialized, so selectedLabel becomes selected-label.
<body>
...
<template id="tpl" is="dom-bind">
<my-navigation selected-label="{{myLabel}}" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{myLabel}}" id="my_scaffold"></my-scaffold>
</template>
<script>
...
window.addEventListener("WebComponentsReady", function (e) {
document.querySelector("#tpl").myLabel = "Home";
...
});
...
</script>
</body>
There is probably a better way to do that, but you can try this:
<body>
<template id="app" is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
<script>
var app = document.querySelector('#app');
app.addEventListener('template-bound', function () {
console.log('Our app is ready to rock!');
});
window.addEventListener('WebComponentsReady', function () {
document.querySelector('body').removeAttribute('unresolved');
var my-navigation = document.querySelector('my-navigation');
// This will add the variable to the 'app' context (template)
app.selectedLabel = my-navigation.selectedLabel;
});
</script>
</body>

Access core-ajax response from within nested component?

I have a core-ajax element like so:
<core-ajax id="ds" auto url="address/to/data.json" response="{{data}}"></core-ajax>
Then a list element like so:
<my-items alarms="{{data}}"></my-items>
Which iterates through the data and displays it in another template:
<template repeat="{{item in items}}">
<my-item alarm="{{item}}"></my-item>
</template>
My question is, from within "my-item", how can I access the original {{data}}? For instance, if I wanted to get a count of the total items in a javascript function?
You need to pass that information into <my-item>.
If <my-item> also published a data property, you could pass it like this:
<template repeat="{{alarm in alarms}}">
<my-item alarm="{{alarm}}" data="{{alarms}}"></my-item>
</template>
or just the stats you need:
<my-item alarm="{{alarm}}" length="{{alarms.length}}"></my-item>
Polymer('my-item', {
dataChanged: function (e) {
console.log(this.data);
}
});
that should give you the data you need

Caching elements in templates

I have my main element using the flatiron-element to redirect my user:
....
<template if="{{route != null}}">
<template if="{{route == 'home' || route == ''}}">
<home-element structure="{{home}}"></home-element>
</template>
<template if="{{route == 'research'}}">
<research-element structure="{{research}}"></research-element>
</template>
<template if="{{route == 'highlights'}}">
<!-- <highlights-element></highlights-element> -->
</template>
</template>
....
Each time I change {{route}}, the elements get recreated. Is there a good way to cache it so we do not have to reload it if it was previously loaded?
Thanks
What you're seeing is Polymer's template system instantiating each element as needed. When you cycle routes, it adds/removes the element from the DOM. The next time a round, the data-binding system stamps out a new instance of the element. This also means created() and ready() get called "again".
One option is to use <polymer-ui-pages> for this: http://www.polymer-project.org/docs/elements/polymer-ui-elements.html#polymer-ui-pages
Another is to show/hide elements as needed in CSS (instead of using conditional templates): http://jsbin.com/zeyoyisu/2/edit