Dynamically Generating Bind Variable Names - polymer

I have a data grid, in which I use dom-repeat to generate the columns.
<vaadin-grid-filter value=[[filterInput]] />
<input value={{filterInput::input}} />
</vaadin-grid-filter>
I bind the value used to filter a column with the value input into an input element.
My problem is each column binds to the same filterInput variable.
Is there any way I can bind using a variable for each specific column?
Could I somehow generate the binding variable for each column, e.g. filterInput[0], filterInput[1] etc. by using the index variable that comes with dom-repeat?

I make it working with an element.
HTML template
<template is="dom-repeat" items="{{technology}}">
<input type="text" value="{{item.label::input}}">[[item.label]]<br/>
</template>
Polymer Element
technology : {
type: Array,
value: [
{id:"php", label:"PHP", selected:false},
{id:"js", label:"Javascript", selected:false},
{id:"html", label:"HTML", selected:false},
{id:"css", label:"CSS", selected:false},
]
}
Full Polymer element
<dom-module id="input-array-element">
<template>
<h3>Inputs Array</h3>
<template is="dom-repeat" items="{{technology}}">
<input type="text" value="{{item.label::input}}">[[item.label]]<br/>
</template><br>
</template>
<script>
class InputArrayElement extends Polymer.Element {
static get is() { return 'input-array-element'; }
static get properties() {
return {
technology : {
type: Array,
value: [
{id:"php", label:"PHP", selected:false},
{id:"js", label:"Javascript", selected:false},
{id:"html", label:"HTML", selected:false},
{id:"css", label:"CSS", selected:false},
],
notify: true
}
}
}
ready() {
super.ready();
this.addEventListener("technology-changed", function(e){
console.log(e);
});
}
}
window.customElements.define(InputArrayElement.is, InputArrayElement);
</script>
</dom-module>

Related

Reading and displaying json data using Polymer

I am new to polymer and I am trying to read JSON data in a custom-element and display it in other element.
This is my JSON data:
jsonData.json
[
{
"name":"Ladies+Chrome+T-Shirt",
"title":"Ladies Chrome T-Shirt"
},
{
"name":"Ladies+Google+New+York+T-Shirt",
"title":"Ladies Google New York T-Shirt"
}
]
This is my shop-app.html file where I try to read data from JSON file (I am not sure if this is correct or not as I am not able to test it):
<dom-module id="shop-category-data">
<script>
(function(){
class ShopCategoryData extends Polymer.Element {
static get is() { return 'shop-category-data'; }
static get properties() { return {
data: {
type: Object,
computed: '_computeData()',
notify: true
}
}}
_computeData() {
this._getResource( {
url: 'data/jsonData.json',
onLoad(e){
this.set('data.items', JSON.parse(e.target.responseText));
}
})
}
_getResource(rq) {
let xhr = new XMLHttpRequest();
xhr.addEventListener('load', rq.onLoad.bind(this));
xhr.open('GET', rq.url);xhr.send();
}
}
customElements.define(ShopCategoryData.is, ShopCategoryData);
})();
</script>
</dom-module>
This is the element where I want to display the data I read from the JSON file:
<dom-module id="shop-app">
<template>
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}">
</app-route>
<shop-category-data data="{{data}}"></shop-category-data>
<template>
<div> Employee list: </div>
<template is="dom-repeat" items="{{data}}">
<div>First name: <span>{{item.name}}</span></div>
<div>Last name: <span>{{item.title}}</span></div>
</template>
</template>
</template>
<script>
class ShopApp extends Polymer.Element {
static get is() { return 'shop-app'; }
}
customElements.define(ShopApp.is, ShopApp);
</script>
</dom-module>
The line <shop-category-data data="{{data}}"></shop-category-data> should give me the data, which I then try to display using dom-repeat. But nothing is being displayed. So, I think there is some mistake in my reading the JSON data.
Edit:
The JSON is read correctly, it is just not getting reflected back in my:
<shop-category-data data="{{data}}"></shop-category-data>
Computed properties is not returning a value. If you want to define data as a computed property you must return a value from the computed property function _computeData(). But in your case you are using asynchronous XMLHttpRequest. So, if you return a value after calling this._getResource... you need to make it synchronous (which no one recommends).
Plnkr for synchronous method: http://plnkr.co/edit/jdSRMR?p=preview
Another way is calling the method inside ready(). This is asynchronous.
Plnkr: http://plnkr.co/edit/pj4dgl?p=preview
It's not getting reflected back because the json is assigned to data.items, rather than to data itself.
this.set('data', JSON.parse(e.target.responseText));
It's recommended to use <iron-ajax>, and scrap <shop-category-data>. e.g. replace the following line
<shop-category-data data="{{data}}"></shop-category-data>
with
<iron-ajax auto url="data/jsonData.json" handle-as="json"
last-response="{{data}}"></iron-ajax>

Computed properties on an item of dom-repeat template

If I have the following dom-repeat template:
<template is="dom-repeat" items="{{myFiles}}" as="file">
<span>
{{file.createDate}} <br/>
</span>
</template>
and I would like to format file.createDate, is there a way to use computed property to do this?
No, you would need to use a computed binding on the item (or in this case, its subproperty):
// template
<template is="dom-repeat" items="{{_myFiles}}" as="file">
<span>{{_formatDate(file.createDate)}}</span>
</template>
// script
Polymer({
_formatDate: function(createDate) {
return /* format createDate */;
}
});
Alternatively, you could use a computed property (e.g., named _myFiles) on the myFiles array, which would process all the items before the dom-repeat iteration:
// template
<template is="dom-repeat" items="{{_myFiles}}" as="file">
<span>[[file.createDate]]</span>
</template>
// script
Polymer({
properties: {
myFiles: Array,
_myFiles: {
computed: '_preprocessFiles(myFiles)'
}
},
_preprocessFiles: function(files) {
return files.map(x => {
x.createDate = /* format x.createDate */;
return x;
});
}
});

Two way data binding with Polymer.Templatizer

I am trying to get two way data-binding between a host element and a template in Polymer using templatizer. For example if I am trying to keep two input boxes in-sync:
<html>
<body>
<my-element>
<template >
<input type="text" value="{{test::change}}" />
<div>The value of 'test' is: <span>{{test}}</span></div>
</template>
</my-element>
<dom-module id="my-element">
<template>
<input type="text" value="{{test::change}}" />
value:
<p>{{test}}</p>
<div id="items"></div>
<content id="template"></content>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
test: {
type: String,
value: "a"
},
behaviors: [ Polymer.Templatizer ],
_forwardParentProp: function(prop, value) {debugger},
_forwardParentPath: function(path, value) {debugger},
_forwardInstanceProp: function(inst, prop, value) {debugger},
_forwardInstancePath: function(inst, path, value) {debugger},
ready: function() {
this._instanceProps = {
test: true
};
var templates = Polymer.dom(this.$.template).getDistributedNodes();
template = templates[1];
this.templatize(template);
var itemNode = this.stamp({ test: this.test});
Polymer.dom(this.$.items).appendChild(itemNode.root);
}
});
</script>
</body>
</html>
In the above code I hit the debugger in the _forwardInstanceProp but not any of the others. Why is this? Inside _forwardInstanceProp I can access my-element and manually update the test property. Is there a better way to do this? I also could add an observer on my-element to the test property and then propagate any changes in my-element to the template. Is there a better way to do that? I am just trying to understand what all four of these methods do and when/why they should be used.
It beats my why I can never get neither _forwardParentPath nor _forwardParentProp to run. However, I know when the other two run :)
_forwardInstanceProp runs for direct properties of model passed to stamp and _instanceProps is initialized:
this._instanceProps = {
text: true
};
var clone = this.stamp({
text: this.text
});
_forwardInstancePath on the other hand runs when you pass nested objects to stamp:
var clone = this.stamp({
nested: {
text: this.text
}
});
See this bin for an example: http://jsbin.com/kipato/2/edit?html,js,console,output
In the stamped template there are two inputs bound to two variables which trigger instanceProp and instancePath. Unfortunately I've been unable to fix the error thrown when the latter happens.

dom-repeat template fails to render array with error 'expected array for items'

I have a simple template that renders an array object. However, it fails with the following message:
[dom-repeat::dom-repeat]: expected array for `items`, found [{"code":1,"name":"Item #1"},{"code":2,"name":"Item #2"},{"code":3,"name":"Item #3"}]
The array is passed in the attribute of the custom element in the following format:
[{"code":1,"name":"Item #1"},{"code":2,"name":"Item #2"},{"code":3,"name":"Item #3"}]
I have read the docs on template repeaters several times and still unable to find what I am doing wrong.
Any help would be much appreciated!
Here is my custom element:
<dom-module id="x-myelement">
<template>
<div>
<h1>{{title}}</h1>
<ul>
<template is="dom-repeat" as="menuitem" items="{{items}}">
<li><span>{{menuitem.code}}</span></li>
</template>
</ul>
</div>
</template>
<script>
(function() {
Polymer({
is: 'x-myelement',
title: String,
items: {
type: Array,
notify: true,
value: function(){ return []; }
}
});
})();
</script>
</dom-module>
And here is now I use it:
<x-myelement title="Hello Polymer"
items='[{"code":1,"name":"Item #1"},{"code":2,"name":"Item #2"},{"code":3,"name":"Item #3"}]'>
</x-myelement>
You need to put your element properties into the properties object (see the Polymer documentation on properties):
Polymer({
is: 'x-myelement',
properties: {
title: String,
items: {
type: Array,
notify: true,
value: function() {return [];}
}
}
});
Otherwise Polymer has no information about your properties. It treated items as a string and didn't parse the attribute value as a JSON array. Eventually dom-repeat was passed a string for its items property as well, resulting in the error that you saw.

Custom filter not working in objects

How to make my custom filter work using bind?
Not Working Example:
JSON:
{ "name": "Adrian" }
HTML:
<template bind="{{user}}">
<p>{{name | filterName}}</p>
</template>
But it works normally when i use repeat.
Working Example:
JSON:
[
{ "name": "Adrian 1" },
{ "name": "Adrian 2" }
]
HTML:
<template repeat="{{user in users}}">
<p>{{user.name | filterName}}</p>
</template>
If you had defined filterName as a function under the elements's prototype...
Polymer('my-element', {
filterName: function(value){
return value.toUpperCase()
}
});
When we do
<template>
{{ user.name | filterName }}
<template>
you have access to your element and its properties 'user', 'users' and the filterName callback.
When you do
<template>
<template bind="{{user}}">
{{name | filterName}}
</template>
</template>
Your outer template has access to user and filterName.
But your inner template is now bound to see only the user object. Your scope is limited to user now. This is a special case when you use bind.
More info here... https://github.com/PolymerLabs/polymer-patterns/blob/master/snippets/basics/using-bind-to-create-a-single-template-instance.html
Nevertheless, there are options for you:
1- Less than ideal -> add the callback as a property in your object. Your model now is responsible for dom transformations. Sucks!
2- If you were to reuse the filter you can turn it into a global expression
PolymerExpressions.prototype.filterName = function (value) {
return value.toUpperCase();
};
And now you can use anywhere.