Because I failed to create a working plunker I uploaded the test elemet to my private webserver. You can test the code below at thevplan.de/x-test.html. The JSON file is located at thevplan.de/getMenu.json.
x-test.html
<!DOCTYPE html>
<html>
<head>
<title>x-test 4</title>
<script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="src/vplan-imports.html">
</head>
<body>
<dom-module id="x-test">
<template>
<iron-ajax
auto
id="getMenu"
url="getMenu.json"
handle-as="json"
on-response="handleResponse"
last-response="{{lastResponse}}"></iron-ajax>
<paper-dropdown-menu label="Day">
<paper-listbox class="dropdown-content" selected="{{selectedDateIndex}}">
<template id="dayList" is="dom-repeat" items="[[lastResponse]]">
<paper-item>[[item.day]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
<br>
<paper-dropdown-menu label="Class">
<paper-listbox id="classMenu" class="dropdown-content" selected="{{selectedClassValue}}"
attr-for-selected="value">
<template is="dom-repeat" items="[[targetArray]]">
<paper-item value="[[item]]">[[item]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
<br>
<br>
<span>selectedClassValue: [[selectedClassValue]]</span>
</template>
</dom-module>
<script>
Polymer({
is: 'x-test',
ready: function () {
},
properties: {
lastResponse: {
type: Array
},
targetArray: {
type: Array,
computed: 'computeTargetArray(selectedDateIndex, lastResponse)',
},
selectedDateIndex: {
type: Number,
value: 0
},
selectedClassValue: {
type: String
},
selectedClassValueOld: {
type: String
}
},
observers: [
'generateClassSelection(targetArray)'
],
handleResponse: function () {
//console.log('x-test: AJAX response ready');
},
computeTargetArray: function (selectedDateIndex, lastResponse) {
this.selectedClassValueOld = this.selectedClassValue;
this.selectedClassValue = false;
return (lastResponse[selectedDateIndex].lessons);
},
generateClassSelection: function (targetArray) {
console.log('x-test: targetArrayChanged');
if (targetArray.indexOf(this.selectedClassValueOld) != -1) {
Polymer.dom(this.root).querySelector('#classMenu').select(this.selectedClassValueOld);
console.log('x-test: selectedClassValueOld used');
} else {
Polymer.dom(this.root).querySelector('#classMenu').select(targetArray[0]);
console.log('x-test: first class selected');
}
}
});
</script>
<x-test></x-test>
</body>
</html>
getMenu.json
[
{
"date": "2016-08-15",
"day": "Monday",
"lessons": [
"08b",
"08c",
"08d",
"09b",
"09c",
"09e",
"10b",
"11"
]
},
{
"date": "2016-08-16",
"day": "Tuesday",
"lessons": [
"06c",
"06d",
"07a",
"07b",
"09a",
"09b",
"09c",
"09d",
"09e",
"10a",
"10c",
"10d",
"11",
"12"
]
},
{
"date": "2016-08-17",
"day": "Wednesday",
"lessons": [
"06a",
"06b",
"06d",
"07a",
"07d",
"08b",
"08c",
"08d",
"09c",
"09e",
"10a",
"10d",
"11",
"12"
]
},
{
"date": "2016-08-18",
"day": "Thursday",
"lessons": [
"05a",
"06c",
"06d",
"07a",
"08c",
"09d",
"09e",
"10a"
]
}
]
The second menu becomes blank if you switch to a target array with the same amount of entries. If you switch from Monday to Tuesday everything works fine. But if you switch from Monday to Thursday the value is not visible inside the dropdown menu. The menu array for Tuesday has more entries than the array for Monday. But the array for Thursday contains the same amount of entries as the array for Monday.
I think you are running into a timing issue.
You should either wait until dom-repeat finished rendering by waiting for the dom-change event (see the docs)
<template is="dom-repeat" items="[[targetArray]]" on-dom-change="generateClassSelection">
</template>
Or you can use this.async to defer setting the selected value of your paper-listbox:
generateClassSelection: function (targetArray) {
console.log('x-test: targetArrayChanged');
this.async(function() {
if (targetArray.indexOf(this.selectedClassValueOld) != -1) {
Polymer.dom(this.root).querySelector('#classMenu').select(this.selectedClassValueOld);
console.log('x-test: selectedClassValueOld used');
} else {
Polymer.dom(this.root).querySelector('#classMenu').select(targetArray[0]);
console.log('x-test: first class selected');
}
}
Related
I have a array with a nested parent-child structure. I need to recursively go through this array and find wanted element. I wrote this function:
component child, this component calls itself, and displays on behalf of each child:
List.vue
<template>
<div>
<div v-for="(item, index) in items" :key="index" >
<div v-if="item.data != null " >
<template v-if="Object.keys(item.data).length > 0">
<template v-for="(item, index) in item.data" >
<v-simple-table :key="index">
<thead>
<tr class="d-flex flex-row">
{{ '░'.repeat(count) }} {{count.length}} {{ item['name'] }}
</tr>
</thead>
<tbody>
</tbody>
</v-simple-table>
</template>
</template>
</div>
<div v-if="item.children != null " >
<template v-if="Object.keys(item.children).length > 0">
<template v-for="(itemD, indexD) in item.children" >
<List :key="indexD" name="List" :items="itemD" :count="count + 1" />
</template>
</template>
</div>
<div v-else></div>
</div>
</div>
</template>
<script>
export default {
name: 'List',
props: {
items: {
type: [Array, Object],
default: [],
},
count: {
type: [Number]
}
},
}
</script>
component parent, this component call component List:
<template>
<div>
<v-row >
<v-col>
<v-card>
<v-card-title>
Estruct
</v-card-title>
<v-row>
<List :items="desserts" :count="count + 1" />
</v-row>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script>
import axios from "axios";
import List from "./List.vue";
export default {
components: { List },
data: (vm) => ({
desserts: [],
count: 0
}),
watch: {
config() {
this.getData();
},
},
methods: {
getData() {
let vm = this;
//
//
axios["get"](process.env.api_url + "/estruct", {
})
.then((response) => {
console.log("response", response.data);
vm.desserts = response.data;
})
.catch((error) => {
console.log(error);
});
},
}
</script>
struct array is like, I am looking at this structure from console.log()
[
{
"data":[{}],
"children":[]
},
{
"data":[
{
"id":64,
"name":"YSVERIS"
}
],
"children":[
{
"data":[
{
"id":85,
"name":"MERCEDES"
}
],
"children":[]
},
{}
]
},
{
"data":[
{
"id":9,
"name":"YUDITH"
}
],
"children":[]
},
{
"data":[
{
"id":10,
"name":"YERNY DEL SOCORRO"
}
],
"children":[]
},
{
"data":[
{
"id":11,
"name":"NIDIA"
}
],
"children":[
{
"data":[
{
"id":21,
"name":"VIVIKEY"
}
],
"children":[]
},
{
"data":[
{
"id":18,
"name":"RODIMIRA "
}
],
"children":[]
}
]
},
{
"data":[],
"children":[]
},
{
"data":[
{
"id":78,
"name":"LUCRECIA"
}
],
"children":[]
},
{
"data":[
{
"id":22,
"name":"DAXI XIOMARA"
}
],
"children":[]
}
]
currently the parent data is being displayed, but not the child data, it should work correct as far as I understand. what do you think??? ....... recursive function is a function that calls itself until it doesn’t. And this technique is called recursion. A recursive function always has a condition to stop calling itself. Otherwise, it will call itself indefinitely. so I don't understand what I'm doing wrong....
I tested in component List.vue, before recursive in <List key="indexD" name="List"....... I added this {{ items }}, so I could see the data object, something like: [ { "data": [ { "id": 85, "name": "MERCEDES"} ] }]
Here is the code in codesandbox code in codesanbox
I have data in this form:
itemlist : {
"dates": [
"2019-03-15",
"2019-04-01",
"2019-05-15"
],
"id": [
"arn21",
"3sa4a",
"wqa99"
],
"price": [
22,
10,
31
]
}
I want to use v-for in my created component that loops through this object treating every index in those 3 nested arrays as one observation. So dates[0], id[0] and price[0]correspond to same item, dates[1] id[1] price[1] is second and so on.
So In other words I think I need to transform this into, but not sure:
0 : {
dates: "2019-03-15",
id: "arn21",
price: 22,}
1:{
dates: "2019-04-01",
id: "3sa4a",
price: 10}
}
2:...
Thats how I pass the data to the component:
<tempc v-for="i in itemlist" :key="i.id" :price="i.price" :dates="i.dates"></temp>
But this does not work for the original data
You can create a computed property for this:
Vue.component('my-component', {
template: '#my-component',
data() {
return {
itemlist: {
"dates": [
"2019-03-15",
"2019-04-01",
"2019-05-15"
],
"id": [
"arn21",
"3sa4a",
"wqa99"
],
"price": [
22,
10,
31
]
}
};
},
computed: {
// Note: here I assume these arrays will always have the same length
mergedList() {
return this.itemlist.dates.map((dates, i) => {
return {
dates,
id: this.itemlist.id[i],
price: this.itemlist.price[i]
};
});
}
}
});
var vm = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<div id="app">
<my-component></my-component>
</div>
<template id="my-component">
<ul>
<li v-for="i in mergedList" :key="i.id" :price="i.price" :dates="i.dates">
{{i.id}} - ${{i.price}} ({{i.dates}})
</li>
</ul>
</template>
I'd like to create a control that converts the data into something I need.
At the moment, I solve this with a global variable. The code looks something like this:
(The lowercase functionality is only to demonstrate it in a simple way. Usually I want to use it for arrays of objects. e.g. to get distinct values of certain names and ids)
<dom-module id="my-tolower">
<script>
"use strict";
Polymer({
is: 'my-tolower',
properties: {
input: {
type: String,
value: "",
},
output:{
type: String,
value: "",
notify: true,
}
},
observers:[
"_inputChanged(input)",
],
_inputChanged: function(newInput, oldInput){
this.set("output", newInput.toLowerCase());
}
});
</script>
</dom-module>
Usage:
<my-tolower input="[[output.name]]" output="{{lower}}">[[lower]]</my-tolower>
This solution works great if I am only using the variable lower only once. Inside of a <dom-repeat>, I get a problem.
How can I easily make a custom variable that is only available inside of my-tolower? Exactly the same way as Polymer's dom-repeat does?
I took a look at the code at Polymer's <dom-repeat> sources, but I have no idea how that works. Is this even possible in a custom element? Do I need to create a custom template?
To explain my problem better I have added a bigger Example that explains my problem in detail.
HTMLImports.whenReady(() => {
Polymer({
is: 'my-app',
ready: function(){
//In my real Problem this value comes from a websocket...
this.devices = [{
name: "PC 1",
components: [
{
name: "HDD1",
processors: [
{
type: "Processor1",
usage: "Dont Know 1"
},
{ type: "Processor1", usage: "DontKnow2"},
{ type: "Processor2", usage: "DontKnow3"}
]
},
{
name: "Another Piece Of Hardware",
processors: [
{
type: "Processor4",
usage: "Dont Know 1"
},
{ type: "Processor3", usage: "DontKnow2"},
{ type: "Processor4", usage: "DontKnow3"}
]
}
]
},
{
name: "PC 2",
components: [
{
name: "My third piece of hardware",
processors: [
{
type: "Processor1",
usage: "Dont Know 1"
},
{ type: "Processor2", usage: "DontKnow2"},
{ type: "Processor3", usage: "DontKnow3"}
]
}
]
}];
//this.devices must not be changed!
}
});
Polymer({
is: 'my-distinct',
properties: {
inputs: {
type: String
},
outputs:{
computed: '_getDistincts(inputs, path)',
notify: true
},
path: {
type: String,
value: ""
}
},
_getDistincts(inputs, path){
let result = [];
for(let key in inputs){
if(inputs.hasOwnProperty(key)){
let x = inputs[key];
if(path && path != ""){
x = x[path];
}
if(result.indexOf(x) < 0){
result.push(x);
}
else{
//Already Exists
}
}
}
return result;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.8.1/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<my-app></my-app>
As you can see, there is always "Processor1", "Processor2" and "Pocessor3" available although this is only the result of the last computers component. You can see the right result (but with duplicates) if you use the comment I made instead.
<dom-module id="my-app">
<template>
<ul>
<template is="dom-repeat" items="[[devices]]" as="device">
<li>[[device.name]]
<ul>
<template is="dom-repeat" items="[[device.components]]" as="component">
<li>[[component.name]]
<ul>
<!-- This is my code with using distinct -->
<my-distinct inputs="[[component.processors]]"
outputs="{{distinctProcessorNames}}"
path="type">
<template is="dom-repeat" items="[[distinctProcessorNames]]" as="processorName">
<li>[[processorName]]
<!-- Here I could iterate over all processors (filtered) and list their usages-->
</li>
</template>
</my-distinct>
<!-- This is my code without using distinct. -->
<!--template is="dom-repeat" items="[[component.processors]]" as="processor">
<li>[[processor.type]]
<ul>
<li>Used for [[processor.usage]]</li>
</ul>
</li>
</template-->
</ul>
</li>
</template>
</ul>
</li>
</template>
</ul>
</template>
</dom-module>
</body>
Demo
You are using 2 different Polymer custom elements <my-app> and <my-distinct>.
Therefore you should declare the second one with its proper <dom-module> statement:
<dom-module id="my-distinct">
<template>
<template is="dom-repeat" items="[[outputs]]" as="processorName">
<li>[[processorName]]
</template>
</template>
</dom-module>
Then use your computed property (based on the attribute value) outputs as the items value of <template is="dom-repeat">.
Demo below:
HTMLImports.whenReady(() => {
Polymer({
is: 'my-app',
ready: function(){
//In my real Problem this value comes from a websocket...
this.devices = [{
name: "PC 1",
components: [
{
name: "HDD1",
processors: [
{
type: "Processor1",
usage: "Dont Know 1"
},
{ type: "Processor1", usage: "DontKnow2"},
{ type: "Processor2", usage: "DontKnow3"}
]
},
{
name: "Another Piece Of Hardware",
processors: [
{
type: "Processor4",
usage: "Dont Know 1"
},
{ type: "Processor3", usage: "DontKnow2"},
{ type: "Processor4", usage: "DontKnow3"}
]
}
]
},
{
name: "PC 2",
components: [
{
name: "My third piece of hardware",
processors: [
{
type: "Processor1",
usage: "Dont Know 1"
},
{ type: "Processor2", usage: "DontKnow2"},
{ type: "Processor3", usage: "DontKnow3"}
]
}
]
}];
//this.devices must not be changed!
}
});
Polymer({
is: 'my-distinct',
properties: {
inputs: {
type: String
},
outputs:{
computed: '_getDistincts(inputs, path)', notify: true
},
path: {
type: String,
value: ""
}
},
_getDistincts(inputs, path){
let result = [];
for(let key in inputs){
if(inputs.hasOwnProperty(key)){
let x = inputs[key];
if(path && path != ""){
x = x[path];
}
if(result.indexOf(x) < 0){
result.push(x);
}
}
}
//console.log( result )
return result;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.8.1/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<my-app></my-app>
<dom-module id="my-app">
<template>
<ul>
<template is="dom-repeat" items="[[devices]]" as="device">
<li>[[device.name]]
<ul>
<template is="dom-repeat" items="[[device.components]]" as="component">
<li>[[component.name]]
<ul>
<my-distinct inputs="[[component.processors]]" path="type">
</my-distinct>
</ul>
</li>
</template>
</ul>
</li>
</template>
</ul>
</template>
</dom-module>
<dom-module id="my-distinct">
<template>
<template is="dom-repeat" items="[[outputs]]" as="processorName">
<li>[[processorName]]
</template>
</template>
</dom-module>
</body>
As you discovered, properties declared inside a <dom-repeat> (i.e., lower in this case) are not scoped exclusively to the <dom-repeat> or its iterations. Thus, each iteration is overwriting the previous lower value, and lower remains available outside the <dom-repeat>.
However, you could achieve a similar scoping effect by attaching an output property to each item iterator in the <dom-repeat> if item is an Object.
For example, consider an <x-foo> element that takes an input array of Objects and passes each input to <my-tolower>, which writes a new value into _output (an attached property on the iterator):
<template is="dom-repeat" items="[[inputs]]" as="x">
<!-- Attach output to a new property on item (i.e., "_output") -->
<my-tolower input="[[x.input]]" output="{{x._output}}"></my-tolower>
</template>
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
inputs: Array
},
_toObjArray: function(inputs) {
// Map inputs into objects so that we can attach properties to each iterator in a dom-repeat
return inputs.map(input => ({input}));
}
});
Polymer({
is: 'my-tolower',
properties: {
input: {
type: String,
value: "",
},
output: {
computed: '_computeOutput(input)',
notify: true,
}
},
_computeOutput: function(input) {
return input.toLowerCase();
}
});
});
<head>
<base href="https://polygit.org/polymer+1.8.1/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo inputs='["aLPha", "brAVo", "CHarLiE", "DelTA", "epSiLoN"]'></x-foo>
<dom-module id="x-foo">
<template>
<template is="dom-repeat" items="[[_toObjArray(inputs)]]">
<!-- Attach output to a new property on item (i.e., "_output") -->
<my-tolower input="[[item.input]]" output="{{item._output}}"></my-tolower>
<div>
<span>[[item.input]] -> [[item._output]]</span>
</div>
</template>
</template>
</dom-module>
</body>
demo
In your code, you have a nested object, used in nested dom-repeats. The same technique from above can be applied at each nesting level, but your example only needs it at the innermost level. You could give <my-distinct>.outputs its own "local" variable by attaching the output to the iterator (i.e., component):
<my-distinct outputs="{{component.distinctProcessorNames}}" ...>
Then, you'd use that in your inner dom-repeat like this:
<template is="dom-repeat" items="[[component.distinctProcessorNames]]" ...>
HTMLImports.whenReady(() => {
Polymer({
is: 'my-app',
ready: function(){
this.devices = [{
name: "PC 1",
components: [
{
name: "HDD1",
processors: [
{
type: "Processor1",
usage: "Dont Know 1"
},
{ type: "Processor1", usage: "DontKnow2"},
{ type: "Processor2", usage: "DontKnow3"}
]
},
{
name: "Another Piece Of Hardware",
processors: [
{
type: "Processor4",
usage: "Dont Know 1"
},
{ type: "Processor3", usage: "DontKnow2"},
{ type: "Processor4", usage: "DontKnow3"}
]
}
]
},
{
name: "PC 2",
components: [
{
name: "My third piece of hardware",
processors: [
{
type: "Processor1",
usage: "Dont Know 1"
},
{ type: "Processor2", usage: "DontKnow2"},
{ type: "Processor3", usage: "DontKnow3"}
]
}
]
}];
}
});
Polymer({
is: 'my-distinct',
properties: {
inputs: {
type: String
},
outputs:{
computed: '_getDistincts(inputs, path)',
notify: true
},
path: {
type: String,
value: ""
}
},
_getDistincts(inputs, path){
let result = [];
for(let key in inputs){
if(inputs.hasOwnProperty(key)){
let x = inputs[key];
if(path && path != ""){
x = x[path];
}
if(result.indexOf(x) < 0){
result.push(x);
}
else {
//Already Exists
}
}
}
return result;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.8.1/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<my-app></my-app>
<dom-module id="my-app">
<template>
<ul>
<template is="dom-repeat" items="[[devices]]" as="device">
<li>[[device.name]]
<ul>
<template is="dom-repeat" items="[[device.components]]" as="component">
<li>[[component.name]]
<ul>
<my-distinct inputs="[[component.processors]]" outputs="{{component.distinctProcessorNames}}" path="type">
</my-distinct>
<template is="dom-repeat" items="[[component.distinctProcessorNames]]" as="processorName">
<li>[[processorName]]</li>
</template>
</ul>
</li>
</template>
</ul>
</li>
</template>
</ul>
</template>
</dom-module>
</body>
your demo with updates
You commented that you don't want to clone any objects or modify the input. That's unfortunately not possible with the iterator-property technique described above. The better option in that case is to provide a template for <my-distinct>, which would encapsulate any transformations without affecting the input.
I have the same problem as this question: "Polymer How to pass returned iron-ajax string into another polymer element", but the answer didn't solve my problem.
I have two custom elements (below), and I want to bind the response from <iron-ajax> into a property (pagination_options) of a-pagination. In a-pagination, if I check the property value using console.log, pagination_options is always logged as undefined. Another property I'm binding (url) is always populated. Why is pagination_options undefined?
table-list element :
<dom-module id="table-list">
<link rel="stylesheet" href="table-list.css" />
<template>
<iron-ajax url=[[url]] last-response={{response}} params=[[params]] auto></iron-ajax>
<template is="dom-repeat" items="{{response.data}}" as="item">
<div>[[item.content]]</div>
</template>
<a-pagination url=[[url]] pagination_options={{response.pagination}}></a-pagination>
</template>
<script>
Polymer({
is: "table-list",
properties: {
url: String,
params: Object
}
});
</script>
</dom-module>
a-pagination element :
<dom-module id="a-pagination">
<script>
Polymer({
is: "a-pagination",
properties: {
url: String,
pagination_options: Object
},
ready: function(){
console.log(this.url);
console.log(this.pagination_options);
}
});
</script>
</dom-module>
Usage:
<table-list url="http://localhost/api/v1/article" params='{"page": 1}'></table-list>
Example AJAX response:
{
"status": "success",
"data": [{
"id": "1",
"content": "content 1"
},
{
"id": "2",
"content": "content 2"
}],
"pagination": {
"total_page": 1,
"per_page": 10,
"current_page": "1"
}
}
In this case, the ready lifecycle event always occurs before the AJAX response event, so when you log the property in ready(), you're actually logging the initial value of pagination_options (which is undefined).
Instead, you should use an observer like this:
Polymer({
is: 'a-pagination',
observers: ['_paginationChanged(pagination_options)'],
_paginationChanged: function(pagination_options) {
console.log(pagination_options);
},
//...
});
HTMLImports.whenReady(() => {
Polymer({
is: "table-list",
properties: {
url: String,
params: Object
},
ready() {
// fill response asynchronously to simulate AJAX event
this.async(() => {
this.response = {
"status": "success",
"data": [{
"id": "1",
"content": "content 1"
}, {
"id": "2",
"content": "content 2"
}],
"pagination": {
"total_page": 1,
"per_page": 10,
"current_page": "1"
}
};
}, 1000);
}
});
Polymer({
is: "a-pagination",
properties: {
url: String,
pagination_options: Object
},
observers: [
'_paginationChanged(pagination_options)'
],
ready() {
// Don't log `pagination_options` in the `ready`
// callback, since the AJAX request that fills
// it might not yet have occurred, and the
// resulting data bindings might not yet have
// taken effect. Use observers instead.
console.log('ready(): url', this.url);
console.log('ready(): pagination_options', this.pagination_options);
},
_paginationChanged(pagination_options) {
console.log('_paginationChanged(): pagination_options', pagination_options);
}
});
});
<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">
</head>
<body>
<div>See console log</div>
<table-list url="http://httpbin.org/get"></table-list>
<dom-module id="table-list">
<link rel="stylesheet" href="table-list.css" />
<template>
<iron-ajax url=[[url]] last-response={{response}} params=[[params]]></iron-ajax>
<template is="dom-repeat" items="{{response.data}}" as="item">
<div>[[item.content]]</div>
</template>
<a-pagination url=[[url]]
pagination_options={{response.pagination}}></a-pagination>
</template>
</dom-module>
</body>
codepen
I follow the Polymer official example of nesting templates and the second template is repeated.
My array data is similar to this:
[
{
"title": "My title book",
"author": "The author",
"votes": [
{ "bad": 0 },
{ "regular": 2 },
{ "good": 201 },
{ "excellent": 458 }
]
},
{
"title": "My title book",
"author":"The author",
"votes": [
{ "bad": 0 },
{ "regular": 2 },
{ "good":201 },
{ "excellent": 458 }
]
}
]
and here is my code of polymer element:
<template is="dom-repeat" items="{{books}}" as="book">
<div><b>Title: </b><span>{{book.title}}</span></div>
<div><b>Author: </b><span>{{book.author}}</span></div>
<div>
<p>Votes:</p>
<template is="dom-repeat" items="{{book.votes}}" as="vote">
<b>Bad: </b><span>{{vote.bad}}</span>
<b>Regular: </b><span>{{vote.regular}}</span>
<b>Good: </b><span>{{vote.good}}</span>
<b>Excellent: </b><span>{{vote.excellent}}</span>
</template>
</div>
</template>
The result of this is:
Title: My book title
Author: My author
Votes:
Bad: 0 Regular: Good: Excellent: Bad: Regular: 2 Good: Excellent: Bad: Regular: Good: 201 Excellent: Bad: Regular: Good: Excellent: 458
Each element in book.votes contains either bad, regular, good, or excellent, but the inner template repeater assumes all voting types are present in each object. That is, the template outputs the tally for all votes in each iteration when only one of those votes is available.
Walking through the four iterations...
The repeater reads book.votes[0] ({"bad": 0}) as vote.
It reads vote.bad and gets a value of 0.
It can't find vote.regular.
It can't find vote.good.
It can't find vote.excellent.
Result:
Bad: 0 Regular: Good: Excellent:
The repeater reads book.votes[1] ({"regular": 2}) as vote.
It can't find vote.bad.
It reads vote.regular and gets a value of 2.
It can't find vote.good.
It can't find vote.excellent.
Result:
Bad: Regular: 2 Good: Excellent:
The repeater reads book.votes[2] ({"good": 201}) as vote.
It can't find vote.bad.
It can't find vote.regular.
It reads vote.good and gets a value of 201.
It can't find vote.excellent.
Result:
Bad: Regular: Good: 201 Excellent:
The repeater reads book.votes[3] ({"excellent": 458}) as vote.
It can't find vote.bad.
It can't find vote.regular.
It can't find vote.good.
It reads vote.excellent and gets a value of 458.
Result:
Bad: Regular: Good: Excellent: 458
If the intention is to show all voting tallies at once, book.votes should be an object instead of an array of objects:
"votes": {
"bad": 0,
"regular": 2,
"good": 201,
"excellent": 458
}
...and the inner template repeater should be removed, binding to book.votes.* directly:
<div>
<b>Bad: </b><span>{{book.votes.bad}}</span>
<b>Regular: </b><span>{{book.votes.regular}}</span>
<b>Good: </b><span>{{book.votes.good}}</span>
<b>Excellent: </b><span>{{book.votes.excellent}}</span>
</div>
<head>
<base href="https://polygit.org/polymer+:master/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="paper-card/paper-card.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<template is="dom-repeat" items="{{books}}" as="book">
<paper-card>
<div><b>Title: </b><span>{{book.title}}</span>
</div>
<div><b>Author: </b><span>{{book.author}}</span>
</div>
<div>
<p>Votes:</p>
<div>
<b>Bad: </b><span>{{book.votes.bad}}</span>
<b>Regular: </b><span>{{book.votes.regular}}</span>
<b>Good: </b><span>{{book.votes.good}}</span>
<b>Excellent: </b><span>{{book.votes.excellent}}</span>
</div>
</div>
</paper-card>
</template>
</template>
<script>
Polymer({
is: 'x-foo',
properties: {
books: {
type: Array,
value: function() {
return [{
"title": "My title book",
"author": "The author",
"votes": {
"bad": 0,
"regular": 2,
"good": 201,
"excellent": 458
}
}, {
"title": "The other book",
"author": "The other author",
"votes": {
"bad": 11,
"regular": 22,
"good": 33,
"excellent": 44
}
}];
}
}
}
});
</script>
</dom-module>
</body>
jsbin before / after