I am using Polymer paper-dropdown-menu.
I need to show drop down for numbers 1 to 5. The crude way to do it is
<paper-dropdown-menu label="Numbers" >
<paper-dropdown class="dropdown">
<core-menu class="menu">
<paper-item>1</paper-item>
<paper-item>2</paper-item>
<paper-item>3</paper-item>
<paper-item>4</paper-item>
<paper-item>5</paper-item>
</core-menu>
</paper-dropdown>
Is there a way to avoid repeating <paper-item> code by using <template>
Something like:
<template repeat="{{ i in [0:25] }}">
<paper-item>i</paper-item>
</template>
you could do a "range" function to produce the array then use the array in the method already posted.
that would look something like
<paper-dropdown-menu label="Numbers" >
<paper-dropdown class="dropdown">
<core-menu class="menu">
<template repeat="{{range}}">
<paper-item>{{}}</paper-item>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
then in js you create the range function
var range = function(begin, end) {
if (typeof end === "undefined") {
end = begin; begin = 0;
}
var result = [], modifier = end > begin ? 1 : -1;
for ( var i = 0; i <= Math.abs(end - begin); i++ ) {
result.push(begin + i * modifier);
}
return result;
}
this range function came from this post which also has several diff methods for doing this. Does JavaScript have a method like "range()" to generate an array based on supplied bounds?
then you assign the range to the polymer variable the repeat template is using
this.range = range(1,25);
hope this helps. sorry i couldn't answer yesterday was leaving for work when i sent last response.
edit: a example on plunker http://plnkr.co/edit/4TkQdR2B5vakbwOSAulK?p=preview
As mentioned in the comments there is an example in the demo provided by polymer.
https://github.com/Polymer/paper-dropdown/blob/master/demo.html
<x-trigger icon="menu">
<paper-dropdown class="with-margin">
with margin<br>
<br>
<template repeat="{{countries}}">
{{name}}<br>
</template>
</paper-dropdown>
</x-trigger>
scope.countries = [
{name: 'Afghanistan', code: 'AF'},
{name: 'Åland Islands', code: 'AX'}
];
Related
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()
Can I use the polymer dom-repeat template without an array?
For example I want to render some code 20 times.
<template is="dom-repeat" items="[[itemCounter]]">
<div>Whatever</div>
</template>
This would render <div>Whatever</div> 20 times, but to achieve this I have to create an array "itemCounter" in the components properties with length 20 with the sole purpose of looping over it.
I was wondering if something like this is possible, so I don't need to create the Array.
<template is="dom-repeat" times="20">
<div>Whatever</div>
</template>
Nope, you can't do this with the normal dom-repeat but I wrote a component which does exactly the trick: https://github.com/MeTaNoV/dom-repeat-n
There is also a discussion about this feature on the Polymer github repository that you can find here: https://github.com/Polymer/polymer/issues/3313
You can do a messy hack like this
Properties:
totalNo: {
type: Number,
value: 20
},
_arrayContainer: {
type: Array ,
value: [],
computed: '_numberToArray(totalNo)'
}
Method:
_numberToArray: function(totalNo) {
var array = [], i;
for (i = 0; i < totalNo; ++i) {
array.push(i);
};
return array;
},
HTML:
<template is="dom-repeat" items="[[_arrayContainer]]">
<div>Whatever</div>
</template>
But I'm not sure you'd every really want to.
I am writing a simple widget that will create an output based on fetched data (taken from an AJAX request).
This version of the my-element is the non-configurable, standard one:
http://jsbin.com/rivala/edit?html,output#H:L56
Thing is, I want the user to be able to decide what the output will look like. Since Polymer doesn't allow us to extend existing elements, I went the other way around: I create a behaviour (err... excuse me, a behavior, it's so hard not to type that "u" every time) that does most of the work. Here is my result:
http://jsbin.com/yuxecu/edit?html,output
So, in order to create create an element, all the user needs to do is:
<dom-module id="my-element">
<template>
<!-- THE FOLLOWING PART IS THE ONLY THING THE USER WILL CHANGE -->
<paper-dropdown-menu label="Your favourite category">
<paper-menu class="dropdown-content">
<template is="dom-repeat" items="{{_data}}">
<paper-item>{{item.name}}</paper-item>
</template>
</paper-dropdown-menu>
</template>
<script>
Polymer({
is: "my-element",
behaviors: [ MyBehaviour],
})
</script>
</dom-module>
And then use it:
I would have much much preferred something a little easier. For example, it would have been much nicer to allow something like this:
<my-element url="http://output.jsbin.com/zonona/3.js">
<template id="bindme">
<!-- THE FOLLOWING PART IS THE ONLY THING THE USER WILL CHANGE -->
<paper-dropdown-menu label="Your favourite category">
<paper-menu class="dropdown-content">
<template is="dom-repeat" items="{{_data}}">
<paper-item>{{item.name}}</paper-item>
</template>
</paper-dropdown-menu>
</template>
</my-element>
But I tried and tried and then tried some more, and it doesn't seem to be possible unless you really want to get your hands dirty.
Once extending non-native elements is possible, I assume I can just create an element declaratively that extends my-element and defines a new template. Till then...
Questions:
Does my code seem to be following at least roughly Polymer's best practices?
Is there a much easier way to do this, that I didn't think of?
Any more comments?
Thank you as ever...
I don't know what I am doing is quite the same thing, but you might be able to draw inspiration from it. I have created a generic dialog box that will provide the results from a database query in it, with the headings data driven and the row size and content also data driven. I actually create this element dynamically in a "manager" element.
Something like this is how the manager retrieves the data and creates the dialog (I call it a report-grid)...
newGrid: function(name, useId, useDates, parent) {
var self = this;
var body;
// jshint unused: false
var dataPromise = new Promise(function(accept, reject) {
var sendOptions = {
url: '/api/queries',
method: 'POST',
handleAs: 'json',
headers: {'content-type': 'application/json'}
};
body = {};
body.name = name;
if (useId) {
body.id = parent.id;
}
if (useDates) {
body.startdate = parent.startdate;
body.enddate = parent.enddate;
}
sendOptions.body = body;
var request = document.createElement('iron-request');
request.send(sendOptions).then(function() {
accept(request.response);
});
});
// jshint unused: true
var x;
var y;
var grid = document.createElement('pas-report-grid');
Polymer.dom(self).appendChild(grid);
if (this.grids.length === 0) {
x = 0;
y = 0;
} else {
x = this.grids[this.grids.length - 1].x + this.deltaX;
y = this.grids[this.grids.length - 1].y + this.deltaY;
}
this.grids.push(grid);
grid.open(dataPromise,body,x,y);
And then the element itself has a load of stuff (not shown) to provide drag and resize handles, but the core of the grid is the following templated stuff
<div class="layout horizontal">
<template is="dom-repeat" items="[[heading]]">
<span class="flex">[[item]]</span>
</template>
</div>
<iron-list id="grid" class="flex" items="[[data]]" as="row">
<template>
<div class="layout horizontal row" tabindex$="[[tabIndex]]" index="[[index]]">
<template is="dom-repeat" items="[[row]]" as="field">
<div class="flex field">[[field]]</div>
</template>
</div>
</template>
</iron-list>
The open function of the grid does this with the data
open: function(dataPromise, params, x, y) {
var self = this;
this.x = x;
this.y = y;
dataPromise.then(function(data) {
self.title = data.name;
self.heading = data.heading;
self.data = data.data;
self.$.griddialog.open();
});
this.params = params;
So what is happening here is the manager is making an iron request (also created dynamically) for a generic query that might or might not need an id and start and end dates, the server responds with a json object which contains a heading array, with a list of heading names, and a data array which is the rows, each row also being an array with the values from the query. I pass that info to the grid element as a promise - so it can get started, attach and so on, and then when the data arrives its loaded into a heading div and an iron list.
The grid element knows nothing about the actual query, how many fields each row will have, or indeed how many rows.
<div id="order">
<template repeat="{{items as item}}">
<p data-id="{{item.id}}" data-qty="{{item.qty}}" data-price="{{item.price}}">
<span>- {{item.id}}</span>
<span> x{{item.qty}}</span>
</p>
</template>
<p>- Total {{total}}€</p>
</div>
Tried itemsChanged first to update the total but that did not work because the observer does not look at the property items[id].qty
The documentation does mention a more specific observer but can not use it when items is a array.
{{items | sum }} fails too because it only updates one time at start up.
Last option is
var order = this.$.order
order.onMutation(order, this.sum)
But then polymer crashes without a error message. I just see a blank screen when I put it in ready:function(){...}
I would say it's not doable right now. or maybe with some crazy hack.
let me suggest that solution:
I have created simple polymer and use power of Computed properties
<polymer-element name="x-repeat">
<template>
<template repeat="{{items as item}}">
<div>{{item.q}}</div>
</template>
<div>Total is {{total}}</div>
</template>
<script>
Polymer({
items : [],
created : function () {
this.items = [{q:1}, {q:2}];
},
computed: { // NOTE: computed set
total: 'items | sum'
},
sum : function (items) { // NOTE : defined 'pipeline' function
var total = 0;
items.forEach(function (i) {
total += i.q;
});
return total;
}
});
</script>
</polymer-element>
Hope that helps!
This seems a trivial thing but I'm unable to find it:
What if I want to reverse the order of my items in a repeat, without actually touching the order of the array, like in:
<template repeat="{{layer in layers}}">
<div>{{layer.name}}</div>
</template>
where layers is an array of objects.
I've tried applying a filter and then working with a copy of the array, like in:
<template repeat="{{layer in layers | reverse}}">
<div>{{layer.name}}</div>
</template>
...
reverse: function(arr){
return _(arr).reverse();
}
but that results in some observers failing since they're looking at the copy instead of the original objects. I don't want to apply a sort to my original array since other parts of the code depend on that order.
Anyone knows of an option where just the order of display in the DOM is affected?
I think you need to do something like this
<template repeat="{{layer in temp_array}}">
<div>{{layer.name}}</div>
</template>
<script>
Polymer('el-name',{
ready: function(){
this.temp_array =[];
this.temp_array = layers.reverse();
}
}
);
</script>
if your layers is empty when ready called, use change listener
<script>
Polymer('el-name',{
ready: function(){
this.temp_array =[];
},
layersChanged: function(oldValue, newValue){
if(newValue.length != 0)
this.temp_array = newValue.reverse();
}
}
);
</script>
Hope it help for you
If it is possible to put the repeated elements in a vertical/horizontal layout, then reverse might do the trick (see layout documentation):
<div vertical layout reverse?="{{ isReversed }}">
<template repeat="{{ layer in layers }}">
<div>{{ layer.name }}</div>
</template>
</div>
I would like to offer a safier and more clear way to revert an array for repeat binding:
<polymer-element name="my-element" attributes="layers layersReversed">
<template>
<template repeat="{{layer in layers}}">
<div>{{layer.name}}</div>
</template>
</template>
<script>
Polymer({
layersReversedChanged: function() {
var layers = this.layersReversed.slice();
layers.reverse();
this.layers = layers;
}
});
</script>
</polymer-element>
<my-element layers="{{layers}}"><!-- direct order --></my-element>
<my-element layersReversed="{{layers}}"><!-- reverse order --></my-element>
Direct or reverse order is defined by used attribute: layers or layersReversed.
There are no value changing in corresponding -Changed event by itself (which may cause falling to endless loop).
The .reverse() method changes the original array, so it should be applied on its copy.
There is another funny and extravagant way to do the same via an intermediate web-component:
<polymer-element name="reverse-order" attributes="in out">
<template></template>
<script>
Polymer({
inChanged: function() {
var out = this.in.slice();
out.reverse();
this.out = out;
}
});
</script>
</polymer-element>
It can be used to bind some elements with different order. I.e., array is populated by .push() method, while preferred array presentation is in reverse order:
<my-element layers="{{layersReversed}}"></my-element>
<reverse-order in="{{layers}}" out="{{layersReversed}}"></reverse-order>
<core-localstorage name="layers" value="{{layers}}"></core-localstorage>