Polymer - How to bind event to a group of button at once - polymer

<core-menu selected="0">
<core-item class="menu-link" icon="info" label="About us"></core-item>
<core-item class="menu-link" icon="image:portrait" label="Professor"></core-item>
<core-item class="menu-link" icon="social:group" label="Members"></core-item>
</core-menu>
I got a core-menu contains a group of core-item, and I want to bind click event to them with the code below
var menulink = document.querySelector('.menu-link');
menulink.addEventListener('click', function(){
console.log(this.label);
});
But it only works to the first core-item, how to resolve this?
Also tried with the
var menulink = document.querySelectorAll('.menu-link');
menulink.addEventListener('click', function(){
console.log(this.label);
});
But i got Uncaught TypeError: undefined is not a function

But it only works to the first core-item, how to resolve this?
That's because, as Peter Burns commented, querySelector only returns the first matching element and ignores the others.
Also tried with [querySelectorAll] but i got Uncaught TypeError: undefined is not a function
This is because querySelectorAll returns an array of matching elements, and addEventListener isn't a method attached to the array object - it's instead attached to each individual element contained within the array. To overcome this we need to loop through each returned element and assign the event listener to each:
var menulinks = document.querySelectorAll('.menu-link');
for (var i = 0; i < menulinks.length; i++)
menulinks[i].addEventListener('click', function(){
console.log(this.label);
});

Related

Function after dom-repeat finished

I created new component in Polymer 2, inside that i will use 2 web components, component a for show name of div and lenght of components b who will run a dom-repeat inside this web component.
With that said, after dom-repeat finishes my code isn't working how i want.
I need to get lenght of components b after dom-repeat run finished to:
- Get lenght and put in my counter in component a
- Separate all components b in groups of 6 inside one div
I made some JQuery code in fiddle, and will put drop below, this worked with default div's, but when i put in ready method of my polymer element is returning 0, isn't waiting dom-repeat finish. I tried setTimeout but this doesn't worked too.
console.log('Init count');
var content = $('component-b').length;
console.log('Cards: ' + content);
var item = $('component-b');
for (var i = 0; i < item.length; i+=6){
item.slice(i, i+6).wrapAll('<div class="list"></div>');
}
var lists = $('.list').length;
console.log('Lists: ' + lists);
$('component-a').attr({
total:content
});
$('component-a').attr({
totalPages:lists
});
Here i will put how this is working now.
Component a have properties to show counter.
<template>
<div class="secao">
<h2>{{title}}</h2>
<component-a>
<template is="dom-repeat" items="{{products}}" as="product">
<component-b class="product" product-id="{{product.productID}}" image-src="{{product.imageSrc}}" href="{{product.href}}"
main-title="{{product.mainTitle}}" authors-text="{{product.author}}" price-discount-value="{{product.priceDiscount}}"
price-value="{{product.realPrice}}" avaliation-score="{{product.avaliationScore}}" flag="{{product.flag}}"></component-b>
</template>
</component-a>
</div>
</template>
That is the result showed in Firefox.
Init count
Cards: 0
Lists: 0
But my Array have 24 cards, and with that jquery code would be 4 lists

Property change not reflecting in UI when its set from dom-repeat's function - Polymer

I have array of objects and a property, my dom-repeat structure is like as below
<template is="dom-repeat" items="{{arrayOfObj}}"> //first dom repeat
<span>[[myProperty]]<span> //here also its not updating
<template is="dom-if" if="[[_checkSomeCondition()]]"> //calling method from dom-if
<span>[[myProperty]]<span> //here its not getting updated value
</template>
</template>
I have a property
properties:{
myProperty:{
type:Boolean
}
}
my function is called each time when dom-repeat iterates
_checkSomeCondition:function() { //I'll check and set property
if(some condition){
this.myProperty = true;
return true;
}
else{
this.myProperty = false;
return true;
}
console.log(this.myProperty); //I'll get the updated value on console
}
but its not changing in screen!! It will display whatever data it set first time inside _checkSomeCondition !! but in console its updating
For testing I inserted a button and after all dom-repeat rendered on tapping that button I called some function ,there when I changed value it get reflected everywhere
this.myProperty = true;
but why its not working when value is changed inside a function which is called by dom-repeat?? I tried all 3 ways of updating a object
Plunker:https://plnkr.co/edit/iAStve97dTTD9cv6iygX?p=preview
Setting a variable via this.myValue = 'somevalue'; won't update binding.
Its best to set variables via this.set('variablename', 'variablevalue');
You could also, after setting a property via this.variablename = 'variablevalue'; this.notifyPath('variablename');
Try
<template is="dom-if" if="[[_checkSomeCondition(myProperty)]]"> //calling method from dom-if
<span>[[myProperty]]<span> //here its not getting updated value
</template>
And then
_checkSomeCondition: function(property) {
I have modified your code like below. Im not sure if it helps for your senario. Please have a look may help to bring up ideas
<template is="dom-repeat" items="{{sensor.Sensor.Contaminants}}" index-as="index">
<span>{{myProperty}}<span> //here also its not updating
<div>{{_checkSomeCondition(index)}}<div> //here its not getting updated value
</template>
And your _checkSomeCondition method will be:
_checkSomeCondition: function() { //I'll check and set property
console.log(index);
if(<your condition>){
this.myProperty = 'true';
} else{
this.myProperty = 'false';
}
// Return whatever you want to show in UI
return this.myProperty;
console.log(this.myProperty); //I'll get the updated value on console
},
and your myProperty should notify the changes. So the property should have notify: true.
With this code i could see the changes in the UI as expected. Let me know if it helps

Can't find node by id in dom-if template

After download Polymer Starter Kit, I included a dom-if template inside (nested) the dom-bind template in index.html to hide the entire UI when user is not authenticated. However, I can’t get dom-if elements using app.$$(‘#id’) feature.
I’m trying to access the paperDrawerPanel in the app.closeDrawer funcion (app.js)
app.closeDrawer = function () {
console.log(app.$$('#paperDrawerPanel')); //returns null
};
How can I access that element?
Edit 1
This is the html part:
...
<body unresolved>
<span id="browser-sync-binding"></span>
<template is="dom-bind" id="app">
<auth-login id="auth" user="{{user}}" is-authenticated="{{isAuthenticated}}"" location="https://i2bserver.firebaseio.com"></auth-login>
<template is="dom-if" if="{{isAuthenticated}}">
<paper-drawer-panel id="paperDrawerPanel" drawer-width="220px" responsive-width="1100px">
...
Edit 2
I guess in the moment that app.closeDrawer is called by the routing.html the dom-if elements should already be tamped.
Here is the code that call app.closeDrawer (routing.html from PSK):
function closeDrawer(ctx, next) {
app.closeDrawer();
next();
}
// Routes
page('*', scrollToTop, closeDrawer, function(ctx, next) {
next();
});
element.$$ queries inside the DOM scope of element. In your case, app is a template whose job is to stamp DOM into the enclosing scope (body).
Try document.querySelector('#paperDrawerPanel').
From the docs:
For locating dynamically-created nodes in your element’s local DOM, use the $$ method:
this.$$(selector)
$$ returns the first node in the local DOM that matches selector.
That means you have to add a # symbol to reference the element using its id. Your code
app.closeDrawer = function () {
console.log(app.$$('paperDrawerPanel')); //returns null
};
should work if written like this
app.closeDrawer = function () {
console.log(app.$$('#paperDrawerPanel'));
};

How to access content of dom-if inside a custom element?

In a custom element I want to access a span and append a child to it but all usual accessors give undefined:
<template>
<template is="dom-if" if="[[condition]]" restamp>
<span id="myspan"></span>
</template>
</template>
ready() {
var a = this.$.myspan; //<------- is undefined
var b = this.$$.myspan; //<------- is undefined
var c = document.getElementById("myspan"); //<------- is undefined
var d = this.$$("#myspan"); //<------- is undefined
}
How to access a span in this case?
UPDATE: here is plunk
The reason this didn't work inside the lifecycle callback without setTimeout or this.async is that right after attaching your element the dom-if template has not yet rendered. Upon attaching your element, Polymer calls the attached callback. However, when the value gets set on the the dom-if, an observer runs and debounces its own _render function. The debounce waits an amount of time to catch any other calls to it, and then it executes the ._render function and attaches the element to the DOM. In other words, when the attached callback runs, normally the dom-if template hasn't rendered yet.
The reason for this debounce is performance. If several changes were made within a very short span of time, this debounce prevents the template from rendering several times when the result we would care about is the end result.
Fortunately, dom-if provides a .render() method which allows you to make it render synchronously. All you need to do is add an id to your dom-if, switch to an attached callback and call like this:
<template>
<template id="someDomIf" is="dom-if" if="[[condition]]" restamp>
<span id="myspan"></span>
</template>
</template>
attached() {
this.$.someDomIf.render();
var c = document.getElementById("myspan"); //<------- should be defined
var d = this.$$("#myspan"); //<------- should be defined
}
Triggering a synchronous render on the dom-if shouldn't be a huge performance problem, since luckily your element should only be getting attached once.
Edit: As it turns it, this even works in a ready callback:
<template>
<template id="someDomIf" is="dom-if" if="[[condition]]" restamp>
<span id="myspan"></span>
</template>
</template>
ready() {
this.$.someDomIf.render();
var c = document.getElementById("myspan"); //<------- should be defined
var d = this.$$("#myspan"); //<------- should be defined
}
See this fork of your plunker:
http://plnkr.co/edit/u3richtnt4COpEfx1CSN?p=preview
Try to do it asynchronously in the attached method as follows, this method works:
attached: function(){
this.async(function(){
var d = this.$$("#myspan");
console.log(d);
},someTimeIfThereAreManyItemsToLoad);
}
The responses above only work if your condition is true initially. Please see my answer to your initial question that lead to this one :
https://stackoverflow.com/a/34137955/3085985
Not sure if you should mix in the .render-stuff from Dogs, but I still think the observer would be the right place for it as it otherwise does not work if condition is false initially.
As in Polymer documentation:
Note: Nodes created dynamically using data binding (including those in dom-repeat and dom-if templates) are not added to the this.$ hash. The hash includes only statically created local DOM nodes (that is, the nodes defined in the element’s outermost template).
You will need to use this.$$('#yourElementId");

Polymer - Updating Element

I'm learning Polymer. One item that is challenging me is updating the item of an array. I wish there was a CDN for Polymer so I could put together a fiddle. For now though, I have an element defined like this:
my-element.html
<dom-module id="my-element">
<button on-click="onLoadData">Load Data</button>
<button on-click="onTest1Click">Test 1</button>
<button on-click="onTest2Click">Test 2</button>
<template is="dom-repeat" items="[[ data ]]" as="element">
<div><span>[[ element.id ]]</span> - <span>[[ element.status ]]</span></div>
<template is="dom-repeat" items="[[ element.children ]]" as="child">
<div> <span>[[ child.id ]]</span> - <span>[[ child.status ]]</span></div>
</template>
</template>
</template>
<script>
Polymer({
is: 'my-element',
properties: {
data: {
type: Array,
value: function() {
return [];
}
}
},
onLoadData: function() {
// Generate some dummy data for the sake of illustration.
for (var i=1; i<=3; i++) {
var element = {
id: i,
state: 'Initialized',
description: ''
};
element.children = [];
for (var j=1; j<=5; j++) {
var child = {
id: i + '-' + j,
state: 'Initialized',
description: ''
}
element.children.push(child);
}
data.push(element);
}
},
// Setting an individual property value works
onTest1Click: function() {
this.set('data.0.children.0.state', 'data set');
},
// Setting an entire object value does NOT work.
onTest2Click: function() {
var c = this.data[0].children[0];
c.state = 'data set';
this.set('data.0.children.0', c);
}
});
</script>
</dom-module>
For some reason, if I update the property value of an array element (as shown in onTest1Click), the UI is updated properly. However, if I update an entire element (as shown in onTest2Click), the UI does NOT get updated. In my real problem, I'm updating multiple properties on an element. For that reason, I'm trying to update an array element and not just a property. Am I doing something wrong or misunderstanding something? Or, am I going to have to update each property value individually?
If you want to mutate an array, rather than just an object in an array (such as swapping out an entire element in an array), there are array mutation methods similar to this.set.
For example, this.splice('data.0.children', 0, 1, c) will remove the current item at the 0 index of the child array and replace it with a new one, which is what it appears you're trying to do. There's also this.shift, this.unshift, this.push and this.pop. These are all similar to their Array prototype counterparts.
One thing to note is that in your example, you're also not actually swapping out the entire object. When you grab the element from the array, mutate a field, and try and replace it with itself, you're not actually replacing it, so that doesn't actually trigger an update. And since the mutation of the field was done outside of Polymer's notification system, that also doesn't trigger an update. If you replace the item with an actual different object, it will work using splice.
https://jsbin.com/rapomiyaga/1/edit?html,output (This is a modified snapshot of Günter Zöchbauer's jsbin)
If you're not making a copy of the object/a completely new object, you'll want to update each field individually through this.set.
Yes, you are going to need to update each property value individually. When you call set, Polymer will go to the given path and check if the value has changed. If the value is an object it will compare the references (and not the subproperties). Since the object reference has not changed, it will not update the UI.