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
Related
I'm using a slot to display a button in Vue Tables 2. How can I pass the id of the warehouse i.e. 123 or 456 to the edit() event handler?
I've tried adding props (as this is what the docs show). But I haven't had any luck. I'm using Vue Tables 2 in a component.
<template>
<div>
<h1>How to pass warehouse id to edit() method?</h1>
<v-client-table :columns="columns" :data="warehouses" :options="options">
<span slot="actions" slot-scope="{ WarehousesMin }">
<button v-on:click="edit">Edit</button>
</span>
</v-client-table>
</div>
export default {
name: 'WarehousesMin',
data() {
return {
warehouses: [
{"id": 123, "name": "El Dorado", "loc": "EDO"},
{"id": 456, "name": "Tartarus", "loc": "TAR"}
],
options: {
headings: {name: 'Name', code: 'Loc', actions: 'Actions'}
},
columns: ['name', 'loc', 'actions'],
}
},
methods: {
edit (warehouseId) {
// How to get id of warehouse here? i.e. 123 or 456
}
}
}
I haven't used this library before, but as far as I know about Vue slots, you can change your code into this and try again:
<template>
<div>
<h1>How to pass warehouse id to edit() method?</h1>
<v-client-table :columns="columns" :data="warehouses" :options="options">
<span slot="actions" slot-scope="{row}">
<button v-on:click="edit(row.id)">Edit</button>
</span>
</v-client-table>
</div>
and in script part, change to:
export default {
name: 'WarehousesMin',
data() {
return {
warehouses: [
{"id": 123, "name": "El Dorado", "loc": "EDO"},
{"id": 456, "name": "Tartarus", "loc": "TAR"}
],
options: {
headings: {name: 'Name', code: 'Loc', actions: 'Actions'}
},
columns: ['id', 'name', 'loc', 'actions'],
}
},
methods: {
edit (warehouseId) {
// The id can be fetched from the slot-scope row object when id is in columns
}
}
}
I think this ought to work, but if not please let me know.
I'm building a simple page with Angular and I got some problems with watchers that I need to create there. First of all my collection looks something like this:
$scope.products = [
{
"name": "Milk",
"price": 2,
"currency": "USD",
"exchangeRate": 1.5,
"localCurrencyPrice": 0
},
{
"name": "Bread",
"price": 1,
"currency": "EUR",
"exchangeRate": 1,
"localCurrencyPrice": 0
},
{
"name": "Skittles",
"price": 3,
"currency": "GBP",
"exchangeRate": 2,
"localCurrencyPrice": 0
},
{
"name": "Nesquik",
"price": 10,
"currency": "CHF",
"exchangeRate": 2.5,
"localCurrencyPrice": 0
}
]
I will be showing a field for each and every property of the object in the collection, so it's going to look something like this: https://plnkr.co/edit/hnuC1IAMZAQznDYP6tsQ?p=preview
As you can see I can add or remove items, so the array length is not a constant number on start up. I want to create watchers for every "PRICE" and "EXCHANGERATE" field in each item and when user changes the value of one of them, they both should be mulplied and the result should be filled up into the "LOCALCURRENCYPRICE" field. /e.g. in the first item "Milk" when I change the price to 1.7, it should be muplied with the exchangeRate of 1.5 and the result should be populated into the localCurrencyPrice model and field/. When you add a new item into the products array, watchers for it's "price" and "exchangeRate" properties should be added as well.
Any ideas how this can be done?
Thanks!
You need to define a ng-change function with your template ( https://docs.angularjs.org/api/ng/directive/ngChange )
For initialization; you can use ng-init ( https://docs.angularjs.org/api/ng/directive/ngInit)
Call it with :
<div class="field" ng-repeat="(fieldKey, fieldValue) in item">
<div ng-init="calcFunc(item)">{{ fieldKey.toUpperCase() }}:
<input type="text" name="item.name" ng-model="fieldValue" ng-change="myFunc(fieldKey, fieldValue,item)/> </div>
</div>
Add it to your controller with your logic :
$scope.calcFunc = function(item) {
item['LOCALCURRENCYPRICE'] = item['EXCHANGERATE'] * item['PRICE'];
}
$scope.myFunc = function(key,val,item){
if(key === 'EXCHANGERATE') .... // check if the updated field is one of your's requested
{
//edit item['LOCALCURRENCYPRICE']
$scope.calcFunc(item);
}
}
i tried it in another way and its in the way you like it:
what i did:
1.Added ng-model to price,exchangerate,localcurrencyrate.
2.Next in the ng-model of localcurrencyrate i multiplied price and exchangerate.thats it
just copy paste below code and test it you will get exactly wat you needed:)
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="customersCtrl">
<table>
<tr ng-repeat="x in products">
<td>
<!-- field name -->
<br>
<!-- fields -->
<b>Item {{$index}}</b>
<button ng-click="RemoveItem()">Remove Item</button><br><br>
NAME:<input type="text" value="{{x.name}}" /><br>
PRICE:<input type="text" value="{{x.price}}" ng-model="x.price"/><br>
CURRENCY:<input type="text" value="{{x.currency}}"/><br>
EXCHANGERATE:<input type="text" value="{{x.exchangeRate}}" ng-model="x.exchangeRate"/><br>
LOCALCURRENCYPRICE:<input type="text" value="{{x.localCurrencyPrice}}" ng-model="x.price*x.exchangeRate"/><br>
<br>
</td>
</tr>
</table>
<button ng-click="AddNewItem()">Add New Item</button>
<br>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('customersCtrl', function($scope)
{
$scope.AddNewItem = function() {
$scope.products.push( {
name: "",
price: 0,
currency: "",
exchangeRate: 0,
localCurrencyPrice: 0
} );
};
$scope.RemoveItem = function() {
$scope.products.splice( this.$index, 1 );
};
$scope.products = [
{
"name": "Milk",
"price": 2,
"currency": "USD",
"exchangeRate": 1.5,
"localCurrencyPrice": 0
},
{
"name": "Bread",
"price": 1,
"currency": "EUR",
"exchangeRate": 1,
"localCurrencyPrice": 0
},
{
"name": "Skittles",
"price": 3,
"currency": "GBP",
"exchangeRate": 2,
"localCurrencyPrice": 0
},
{
"name": "Nesquik",
"price": 10,
"currency": "CHF",
"exchangeRate": 2.5,
"localCurrencyPrice": 0
}
]
});
</script>
</body>
</html>
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
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');
}
}
I have a polymer highcharts element that works:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="bower_components/platform/platform.js"></script>
<link rel="import" href="bower_components/polymer/polymer.html">
<polymer-element name="bar-chart" attributes="source">
<template>
<div id="container" style="max-width: 600px; height: 360px;"></div>
</template>
<script>
Polymer("bar-chart", {
ready: function() {
var options = {
chart: {type: 'bar', renderTo: this.$.container},
title: {text: ''},
subtitle: {text: ''},
xAxis: {categories: []},
yAxis: {title: {text: ''}},
plotOptions: {bar: {dataLabels: {enabled: true}}},
legend: {enabled: false},
credits: {enabled: false},
series: [{}]
};
$.getJSON(this.source).done(function(chartjson) {
options.xAxis.categories = chartjson.categories;
options.series[0].data = chartjson.series;
options.title.text = chartjson.title;
options.subtitle.text = chartjson.subtitle;
options.yAxis.title.text = chartjson.yAxistitle;
var chart = new Highcharts.Chart(options);
});
}
});
</script>
</polymer-element>
<bar-chart source="json/grass-roots/2014 Mayor.json"></bar-chart>
I pass it some nice data via the '2014 Mayor.json' file:
{
"categories": ["Muriel E Bowser", "Tommy Wells", "Jack Evans", "Vincent C Gray", "David Catania", "Andy Shallal", "Reta Jo Lewis", "Carol Schwartz", "Vincent Orange", "Christian Carter", "Nestor DJonkam", "Bruce Majors", "Michael L Green"],
"series": [2505, 1654, 1332, 956, 699, 399, 183, 81, 72, 3, 3, 2, 1],
"title": "Mayor (2014)",
"subtitle": "Grassroots Contributors",
"yAxistitle": "Number of DC Residents Contributing to Candidate"
}
And I get a chart.
But what I really want to do is iterate over an array of chart data to produce multiple charts. I've tried very hard to figure out how template repeat works, but I'm new to both Polymer and javascript, and haven't been able to crack it.
Let's say my data file, 'arrayofdata.json' has the following in it:
[
{
"categories": ["Phil Mendelson", "Kris Hammond", "John C Cheeks"], "series": [172, 44, 4],
"title": "Council Chairman (2014)",
"subtitle": "Grassroots Contributors",
"yAxistitle": "Number of DC Residents Contributing to Candidate"
},
{
"categories": ["Muriel E Bowser", "Tommy Wells", "Jack Evans", "Vincent C Gray", "David Catania", "Andy Shallal", "Reta Jo Lewis", "Carol Schwartz", "Vincent Orange", "Christian Carter", "Nestor DJonkam", "Bruce Majors", "Michael L Green"],
"series": [2505, 1654, 1332, 956, 699, 399, 183, 81, 72, 3, 3, 2, 1],
"title": "Mayor (2014)",
"subtitle": "Grassroots Contributors",
"yAxistitle": "Number of DC Residents Contributing to Candidate"
}
]
How do I iterate over that to produce multiple charts using template repeat?
I don't have a solution for Highcharts, but the Polymeric way to do this is to think declaratively. You don't need $.getJSON. You need an element like <google-chart>, that declaratively renders a chart from data and <core-ajax> for fetching the json data.
The whole element definition becomes something like:
<polymer-element name="bar-charts" attributes="source" noscript>
<template>
<core-ajax auto url="{{source}} response="{{items}}" handleAs="json"></core-ajax>
<template repeat="{{item in items}}">
<google-chart type='pie' height='300px' width='400px'
options='{{item.options}}' cols='{{item.cols}}'
rows='{{item.rows}}' data='{{item.data}}'>
</google-chart>
</template>
</template>
</polymer-element>
How wicked is that!? No code :)
The hardest part would be to get the data in the format google-chart expects. See <google-chart> element for examples.
I know it's an old question but here's the updated Polymeric 1.0/2.0 way of doing it, using Highcharts-Chart:
<link rel="import" href="bower_components/highcharts-chart/highcharts-chart.html">
<template is="dom-bind" id="app">
<template is="dom-repeat" items="{{dynamicChartData}}" as="e">
<highcharts-chart index$="[[index]]" type="pie" data="[[zip(e.categories,e.series)]]" title="[[e.title]]" subtitle="[[e.subtitle]]" y-label="[[e.yAxistitle]]"></highcharts-chart>
</template>
<iron-ajax auto url="{{source}}" last-response="{{dynamicChartData}}" handle-as="json"></iron-ajax>
</template>
<script>
var app = document.querySelector("#app")
app.source = "Your URL-------------------"
app.zip = function(a,b) {
return a.map(function (_, i) {return [a[i], b[i]]})
}
</script>
And if you're looking for more examples you can check out http://avdaredevil.github.io/highcharts-chart/.
I don't know much about Polymer, but from the docs it looks like changing <template> to <template repeat="{{ yourarray }}"> might be the critical step in making this happen.