Automatic node finding for dynamic nodes (dom-repeat) Polymer 1.0 - polymer

In the example below, I want to be able to set the id with the attribute 'first' and select it later with the function '_dummy'. The code below is not working and I have no idea why. I follow the instruction in the doc. Can somebody identify the problem?
<dom-module id="my-positions-list">
<template>
<div> Employee list: </div>
<template is="dom-repeat" items="{{employees}}">
<div id$="{{item.first}}">{{_dummy(item)}}</div>
<div>First name: <span>{{item.first}}</span></div>
<div>Last name: <span>{{item.last}}</span></div>
</template>
</template>
<script>
Polymer({
is: 'my-positions-list',
_dummy: function(item){
var elem = this.$$("#"+item.first); // returns undifined
console.log(elem);
},
ready: function() {
this.employees = [
{first: 'Bob ', last: 'Smith', country: 'united states'},
{first: 'Sally', last: 'Johnson', country: 'united kingdom'}
];
}
});
</script>
</dom-module>

You need to add employees to the properties with a default value.
And you're selecting a node who is just getting created. You need to wait for your local dom to be ready to use your _dummy function.
Use the ready function to get to the node you want.
And consider proper ids (no capital letter, e.g.).
For these kind of problem you should consult the documentation for polymer lifecycles (https://www.polymer-project.org/1.0/docs/devguide/registering-elements.html)
<dom-module id="my-positions-list">
<template>
<div> Employee list: </div>
<template is="dom-repeat" items="{{employees}}">
<div id="{{item.first}}"></div>
<div>First name: <span>{{item.first}}</span></div>
<div>Last name: <span>{{item.last}}</span></div>
</template>
</template>
<script>
Polymer({
is: 'my-positions-list',
properties: {
employees: {
type: Array,
value: function() {
return [
{first: 'Bob', last: 'Smith', country: 'united states'},
{first: 'Sally', last: 'Johnson', country: 'united kingdom'}
];
}
}
},
attached: function() {
this.async(function() {
console.log(this.$$("#Bob"));
});
}
});
</script>
</dom-module>

I think that template is constructed in local shadow dom and _dummy is called before it is attached to higher level dom to be available for querySelector or querySelectorAll.

Related

Polymer data-binding not updated on array mutation

I noticed that template/data-binding in Polymer doesn't seem to reflect when a array property is mutated (i.e. push()). Sample code below:
<body>
<dom-module id="my-element">
<template>
<pre>
[my-element]
myArray: [[jsonStringify(myArray)]]
</pre>
</template>
</dom-module>
<my-element id="elm"></my-element>
<button onclick="pushArray()">pushArray</button>
<button onclick="setArray()">setArray</button>
<script>
(function registerElements() {
Polymer({
is: 'my-element',
properties: {
myArray: {
type: Array,
value: function () {
return [];
}
}
},
pushArray: function(value) {
this.push('myArray', {id: value});
},
setArray: function(value) {
this.set('myArray', [{id: value}]);
},
jsonStringify: function(obj) {
return JSON.stringify(obj);
}
});
})();
function pushArray () {
var elm = document.querySelector('#elm');
elm.pushArray('Push');
}
function setArray () {
var elm = document.querySelector('#elm');
elm.setArray('Set');
}
</script>
</body>
Whenever I click the pushArray button, an item "Push" should be added in myArray, but it wasn't reflected in the template [[jsonStringify(myArray)]]. is this an expected behavior? Anyway to work around this?
The Array change observer is a bit tricky. In your code, by using myArray, you implicitly observe (only) the reference for the (whole) array, which only changes when you run setArray.
In order to overcome this, you must also use a deep observer, namely myArray.*. The correct code for your dom-module is therefore the following:
<dom-module id="my-element">
<template>
<pre>
[my-element]
myArray: [[jsonStringify(myArray, myArray.*)]]
</pre>
</template>
</dom-module>
Live demo: http://jsbin.com/yulivuwufu/1/edit?html,css,output
No other code changes are necessary.

Polymer dom-repeat not displaying the dynamically added values

Polymer dom-repeat is not working as expected, when I add a new element to the Array dynamically, dom-repeat not displaying the value.
I am using the Polymer's array mutation methods when pushing items into the array, but still not working.
Please check the codepen link;
<dom-module id="wind-studio-widget">
<template>
<div on-tap='_addNewPad' style="cursor:pointer"><strong>Click Me</strong></div>
<template id="padListContainerId" is="dom-repeat" items="[[padList]]">
<p style="border:1px solid red"> <span>[[item.id]]</span></p>
</template>
</template>
</dom-module>
<script type="text/javascript">
Polymer({
is: 'wind-studio-widget',
properties: {
baseUrl: {type: String},
padList: {
type: Array,
notify: true
},
padListAverage: {type: Object}
},
ready: function () {
//this.baseUrl = localStorage.baseUrl;
//this.loadPadData();
this.padList = [{'id':'1'},{'id':'2'}];
},
_addNewPad: function () {
var newPadList = this.padList;
newPadList.push({'id':'3'});
this.set('padList', []);
this.set('padList', newPadList);
console.log(this.padList);
//this.$.padListContainerId.render();
}
});
</script>
http://codepen.io/nareshchennuri/pen/vxjvdO

Access current item when clicking link in dom-if inside dom-repeat

I have the following template:
<template is="dom-repeat" items="{{myItems}}">
<template is="dom-if" if="{{_shouldHaveLink(item)}}">
Link
</template>
</template>
Now, if the link was not wrapped in a dom-if, I can see the item which was pressed with:
_linkTapped: function (oEvent) {
console.log('Item link tapped:', oEvent.model.get('item'));
}
But inside the dom-if I can't. Seems like item is now out of scope. How can I get it?
This is a known bug with Polymer's dom-repeat yet to be solved, but there's a simple workaround in this scenario.
Since the dom-if template without restamp simply hides its contents when the if condition is false (as an optimization), you could simulate the original behavior while avoiding the dom-if-related bug by replacing the template with a hidden attribute based on the same condition negated:
<div hidden$="{{!_shouldHaveLink(item)}}">
Link
</div>
or:
Link
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: () => [
{ name: 'google', link: 'http://www.google.com' },
{ name: 'facebook' },
{ name: 'twitter', link: 'http://www.twitter.com' },
]
}
},
_hasNoLink: function(item) {
return !item.link;
},
_linkTapped: function(e) {
console.log(e.model.item);
// for demo only...
e.preventDefault();
}
});
});
<head>
<base href="https://polygit.org/polymer+1.7.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<div>Facebook anchor is hidden because it has no link</div>
<template is="dom-repeat" items="[[items]]">
<a href="#"
hidden$="{{_hasNoLink(item)}}"
on-tap="_linkTapped">[[item.name]]</a>
</template>
</template>
</dom-module>
</body>
codepen
And as #DocDude suggested, another alternative is to use <dom-repeat>.modelForElement(e.target) if you have a reference to the <dom-repeat>:
//template
<template id="repeater" is="dom-repeat" items="[[items]]">
// script
_linkTapped: function(e) {
const m = this.$.repeater.modelForElement(e.target);
console.log(m.item);
...
}
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: () => [
{ name: 'google', link: 'http://www.google.com' },
{ name: 'facebook' },
{ name: 'twitter', link: 'http://www.twitter.com' },
]
}
},
_hasLink: function(item) {
return item.link;
},
_linkTapped: function(e) {
const m = this.$.repeater.modelForElement(e.target);
console.log(m.item);
// for demo only...
e.preventDefault();
}
});
});
<head>
<base href="https://polygit.org/polymer+1.3.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<div>Facebook anchor is hidden because it has no link</div>
<template id="repeater" is="dom-repeat" items="[[items]]">
<template is="dom-if" if="{{_hasLink(item)}}">
[[item.name]]
</template>
</template>
</template>
</dom-module>
</body>
codepen

Polymer. Update view after model changed

This is a common question in Polymer about updating view when model was updated (answer is to use this.set or this.push Polymer methods).
But when I have two elements:
First element:
properties:{
items: {
type: Array
}
},
someFunction: {
this.push('items', {'Name': 'something'});
}
Second Element has property which is bound to 'items' from first element
ready: function(){
this.items = firstElement.items;
}
I would like second element 'items' to be updated when 'items' updated on firstElement. I can't manually notify secondElement, because of app restrictions.
So for now I see (in devTools) that model of second element is correct ('items' contains objects), but view isn't updated.
How to update it?
You need to set notity on the property of the first element to receive changes outside of the element itself
properties:{
items: {
type: Array,
notify: true
}
},
then, in secondElement you can
<firstElement items="{{items}}" />
Let Polymer's two-way binding handle the notifications for you.
Consider two elements, x-foo and x-bar, in a container element, x-app.
In x-foo, declare items with notify:true so that its changes would be propagated upward.
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() { return []; },
notify: true
}
}
});
In x-app, bind x-foo's items to x-bar's.
<dom-module id="x-app">
<template>
<x-foo items="{{items}}"></x-foo>
<x-bar items="[[items]]"></x-bar>
</template>
<script>
Polymer({ is: 'x-app' });
</script>
</dom-module>
Now, x-bar would be notified of all changes to the items array (when an item is added/removed).
HTMLImports.whenReady(() => {
"use strict";
Polymer({
is: 'x-app'
});
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() { return []; },
notify: true
}
},
_addItem: function() {
this.push('items', {name: 'item' + this.items.length});
}
});
Polymer({
is: 'x-bar',
properties: {
items: {
type: Array
}
}
});
});
<head>
<base href="https://polygit.org/polymer+1.6.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-app></x-app>
<dom-module id="x-app">
<template>
<x-foo items="{{items}}"></x-foo>
<x-bar items="[[items]]"></x-bar>
</template>
</dom-module>
<dom-module id="x-foo">
<template>
<h2><code>x-foo</code></h2>
<button on-tap="_addItem">Add item</button>
</template>
</dom-module>
<dom-module id="x-bar">
<template>
<h2><code>x-bar</code></h2>
<ul>
<template is="dom-repeat" items="[[items]]">
<li>[[item.name]]</li>
</template>
</ul>
</template>
</dom-module>
</body>
codepen

Polymer 1.0: how to access a property value inside a function

How can I access the value of a property inside a function?
Here is the property
properties:{
name: {type: String}
}
<my-element name="Subrata"></my-element>
Inside <my-element> I have a function like this:
Approach #1
<dom-module id="my-element">
<template>
...
...
</template>
<script>
(function () {
is: 'my-element',
properties:{
name: {type: String}
},
getMyName: function(){
return this.name;
}
})();
</script>
</dom-module>
My another approach was to put the value inside an element but that did not work either.
Approach #2
<dom-module id="my-element">
<template>
<!-- value of name is rendered OK on the page -->
<p id="maxp">{{name}}</p>
</template>
<script>
(function() {
is: 'my-element',
properties: {
name: {type: String}
},
getMyName: function(){
var maxpValue = this.$$("#maxp").innerHTML;
return maxpValue;
}
})();
</script>
</dom-module>
How can I accomplish this? Please help.
Thanks in advance
Instead of using a self-invoking anonymous function, you should be using the Polymer function. Change (function() { ... })(); in your code to read Polymer({ ... });.
Here's an example:
<dom-module id="my-element">
<template>
...
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
properties: {
name: {
type: String
}
},
getMyName: function() {
return this.name;
}
});
</script>
I suggest that you follow the Getting Started guide from the Polymer documentation as it goes over all of this and more. It is a good starting point when you are looking to begin working with Polymer.
You could simple do
this.name
Anywhere in any function to access the variable