Polymer 1.0 hidden$ attribute negate operator - polymer

Just trying to apply negate operator, it seems not working, any input on this appreciated.
it is definitely not a blocker, i can write a compute method to handle it, but negate operator makes more sense to me at-least.
Below snippet not working, if i remove the negate operator it works in opposite way of what i expect.
<div hidden$="{{!productDetails}}">
My Hidden Content
</div>
Documentation (not a comprehensive one)
https://www.polymer-project.org/1.0/docs/devguide/templates.html

You could use DOM if
<dom-module id="user-page">
<template>
All users will see this:
<div>{{user.name}}</div>
<template is="dom-if" if="{{user.isAdmin}}">
Only admins will see this.
<div>{{user.secretAdminStuff}}</div>
</template>
</template>
<script>
Polymer({
is: 'user-page',
properties: {
user: Object
}
});
</script>
</dom-module>

The hidden attribute is complicated with regard to undefined values. Consider the following in your polymer template:
<div hidden="{{ prop1}}">this is hidden on prop1 </div>
<div hidden="{{!prop1}}">this is hidden on not prop1 </div>
If prop1 is undefined, both divs are visible. In other words:
(when prop1 is undefined)
prop1 = false
!prop1 = false
When prop1 is assigned a value, one of the elements will disappear.
(when prop1 is 5)
prop1 = true
!prop1 = false

Related

getContentChildren equivalent in Polymer 2

I have a custom element
<!-- element template -->
<dom-module id="custom-element">
<template>
<style></style>
<div class="toggle">
<slot id="toggleContent" name="toggle"></slot>
</div>
</template>
<script>...</script>
</dom-module>
<!--usage-->
<custom-element>
<div slot="toggle">I'm the toggle</div>
</custom-element>
In Polymer 1.0, I could get the distributed child node by using
[this.getContentChildren('#toggleContent')\[0\];][1]
which would return <div toggle>I'm the toggle</div>
However in Polymer 2. getContentChildren isn't supported anymore and doing it this way
this.$.toggleContent.assignedNodes({flatten: true}).filter(function(n) {
return (n.nodeType === Node.ELEMENT_NODE);
});
doesn't return me the expected element, <div slot="toggle">I'm the toggle</div>.
How do I get the equivalent result using assignedNodes() in Polymer 2?
Please see following plunker
Thanks.
Use the following code:
this.shadowRoot
.querySelector('#toggleContent')
.assignedNodes({flatten:true})
.filter(n => n.nodeType === Node.ELEMENT_NODE)
But if you have single slot then you can just do:
this.shadowRoot
.querySelector('slot')
.assignedNodes({flatten:true})
.filter(n => n.nodeType === Node.ELEMENT_NODE)
The this.$ hash is created when the shadow DOM is initialized. Nodes
created dynamically are not added to the this.$ hash. So, you have to use this.shadowRoot.querySelector.
Update After the demo provided:
_toggleEl is an array/object and you are comparing it with an element. So, it is returning false always. So, use var equal = elementClicked === this.toggleEl[0] like you did in polymer 1.

Two-Way Binding Across Components in Polymer

I have a Polymer app that has some nested components. I'm trying to share a property value across the components. My thought was that I could do this with data-binding. However, I have been unsuccessful in my attempt, which can be seen in this Plunkr. The relevant code (I think) is here:
<neon-animated-pages selected="[[selectedPageIndex]]" style="height:100%;">
<view-1 is-enabled="{{ isEnabled }}"></view-1>
<view-2 is-enabled="{{ isEnabled }}"></view-2>
</neon-animated-pages>
Each view has a paper-toggle-button. If the toggle button is set in one view, I want that same value to appear in the other view. Yet, that is not what's happening. It's like each view is using it's own isEnabled value. As the Plunkr shows, I've done the following:
Created a property called isEnabled in each view ("view-1.html" and "view-2.html").
Created a property called isEnabled in the hosting component ("shell.html")
Used two-way binding via the {{ }} curly-brace syntax.
What am I doing wrong? Why isn't the isEnabled value set in one view propogating to the other?
Thank you!
You should set notify to true in the definition of the isEnabled property in you views. This is needed for two-way data-binding (docs).
First: Name your element files the way you name your elements. Change shell to app-shell in your directory.
Second: What user Maria said, just declare a notify: true property to each element you want to be able to databind, like this:
<dom-module id="view-1">
<template>
<h2>View 1</h2>
<paper-toggle-button checked="{{ isEnabled }}">Is Enabled?</paper-toggle-button>
</template>
<script>
Polymer({
is: 'view-1',
properties: {
isEnabled: {
type: Boolean,
value: false,
notify: true
}
}
});
</script>
</dom-module>
Do the same for the view-2 property.
Here is your plunkr with working two-way data binding:
http://plnkr.co/edit/YhjE02O14YGCErXu9Vtq
Hope it helps you.

how to dynamically append an element to dom-if in Polymer?

My goal is to append an element to existing dom-if dynamically. Problem is that after appending I can see appended element in the DOM three but it never reacts on condition and stays always hidden.
<template>
<template id="domif" is="dom-if" if="[[condition]]" restamp></template>
</template>
ready() {
var el = document.createElement("input");
Polymer.dom(this.$.domif).appendChild(el);
Polymer.dom.flush();
}
Exploring DOM with hardcoded dom-if and input shows that <input /> element is actually not a child of dom-if but lives next to it..
<template>
<template is="dom-if" if="[[condition]]" restamp>
<input />
</template>
</template>
That gave me a clue that I probably should append my element next to dom-if... But now the biggest question is how to say to dom-if that appended element should be rendered if condition is satisfied. Any ideas?
How about adding a span in your dom-if and appending it to that span?
Update after some comments : We need to use this.async for the item to be found. Using the ready-event only works when the condition is true initially. So you could append the element in a conditionChanged-observer - this is a working example :
<dom-module id='my-element1'>
<template>
<template is="dom-if" if="[[condition]]" restamp>
<span id="appendHere"></span>
</template>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element1',
properties: {
condition: {
type: Boolean,
observer: "_conditionChanged"
}
},
_conditionChanged: function(newVal) {
if (newVal) {
this.async(function() {
var el = document.createElement("input");
Polymer.dom(this.$$("#appendHere")).appendChild(el);
Polymer.dom.flush();
});
}
}
});
</script>
Try it here : http://plnkr.co/edit/1IIeM3gSjHIIZ5xpZKa1?p=preview .
A side-effect of using dom-if in this case is that after setting the condition to false, the element disappears completely and gets added on the next condition-change again. So every change before setting the condition to false gets lost. You could work around it by putting the added element somewhere hidden when the condition changes and getting it back later, but I don't think this is a good idea, if the following is an alternative :
The Polymer-team recommends using dom-if only if there is no other way, like hiding the element. So, if it is possible you also could do something like this (condition has to be true to hide the element) :
<dom-module id='my-element1'>
<template>
<span id="appendHere" hidden$="[[condition]]"></span>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element1',
properties: {
condition: Boolean
},
ready: function() {
var el = document.createElement("input");
Polymer.dom(this.$.appendHere).appendChild(el);
Polymer.dom.flush();
}
});
</script>
Try it here :
http://plnkr.co/edit/mCtwqmqtCPaLOUveOqWS?p=preview
The template element itself will not be added to the DOM, this is the reason you can't access it using querySelector or getElementXxx

Data binding between published properties of two custom elements inside an auto binding template - Polymer 1.0

Problem: I have an auto binding template in my main index.html page. Inside the template I am using two of my custom elements. One element is the producer of some data and the other one is the consumer of that data. These custom elements expose published/declared properties for each other to use and bind to. I was able to do that in Polymer 0.5 fairly easily (an example shown below). How do I do the same in Polymer 1.0?
How I used to do in Polymer 0.5?
In Polymer 0.5 I used to data bind between published properties of two custom elements using curly brace syntax and then inside it used the auto node finding concept to directly bind to other element's published property. An example shown below,
<template is="auto-binding">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
I tried something similar in Polymer 1.0 as shown in the example below
<template is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
But it throws an error:-
Uncaught TypeError: Cannot read property '$' of undefined
You can't do $.* bindings inside the template in Polymer 1.0. Instead, either refactor or use computed functions.
In your situation, since selectedLabel and toolbartitle shares the same value, it is much better to simply bind them to the same property.
Also, attribute names that are declaratively passed in (through the element tag) need to be serialized, so selectedLabel becomes selected-label.
<body>
...
<template id="tpl" is="dom-bind">
<my-navigation selected-label="{{myLabel}}" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{myLabel}}" id="my_scaffold"></my-scaffold>
</template>
<script>
...
window.addEventListener("WebComponentsReady", function (e) {
document.querySelector("#tpl").myLabel = "Home";
...
});
...
</script>
</body>
There is probably a better way to do that, but you can try this:
<body>
<template id="app" is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
<script>
var app = document.querySelector('#app');
app.addEventListener('template-bound', function () {
console.log('Our app is ready to rock!');
});
window.addEventListener('WebComponentsReady', function () {
document.querySelector('body').removeAttribute('unresolved');
var my-navigation = document.querySelector('my-navigation');
// This will add the variable to the 'app' context (template)
app.selectedLabel = my-navigation.selectedLabel;
});
</script>
</body>

Polymer: reverting/ordering items in repeat without touching array order

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>