Polymer 1.0: Conditional Flow statements - polymer

Can I do something like the following, in Polymer 1.0:
<template is="dom-bind">
<template is="dom-if" if="{{ messagetype=='chat' }}">
<p>{{message.text}}</p>
</template>
<template is="dom-if" if="{{ messagetype=='location' }}">
<p>{{message.latitude}} , {{message.longitude}}</p>
</template>
</template>
i.e make a decision based on messagetype binding and write nothing else in Polymer() function

Expression syntax in Polymer 1.0 is very limited.
You can access fields (or propeeties) and functions.
The expression can use ! for not and . for . for array index and object property access.
For everything esle create a function that returns the computed result.
See also https://www.polymer-project.org/1.0/docs/devguide/data-binding.html#expressions-in-binding-annotations
For your example you could create a function like
function: isEqual(a, b) {
return a == b;
}
and use it in the binding like
<template is="dom-if" if="{{isEqual(messagetype, 'chat')}}">

This is not a good use of dom-if statements. You are better off creating a function that computes what text to put into the p tag and moving the conditional logic into there.
Something along the lines of this:
<template is="dom-bind">
<p>[[computeMessageValue(message, messagetype)]]</p>
</template>
With the following computing function:
computeMessageValue: function (message, messagetype) {
if (messagetype === 'chat') {
return message.text;
} else if (messagetype === 'location') {
return message.latitude + ' , ' + message.longitude;
}
}
Using the dom-if approach is slower, harder to write tests for and gets very messy very quick as you add in new message types.

Related

Using a pair of Polymer Component twice in the same page doesnt work

Im new to Polymer. Im using two different Polymer components that communicates with each other. I have to do it twice (2 x 2 components).
The following code uses just one pair of different components and it works:
<dom-bind id="dombind">
<template is="dom-bind">
<polymer-componentA id="polymercomponentA_1"
attribute="[[x]]"
attribute="[[x]]"
attribute="{{x}}">
</polymer-componentA>
<polymer-componentB id="polymer-componentB_1"
attribute="{{x}}">
</polymer-componentB>
</template>
</dom-bind>
But when I add other pair of components, it starts working bad. I think they use the same component (instead of being independent):
<dom-bind id="dombind">
<template is="dom-bind">
<polymer-componentA id="polymercomponentA_1"
attribute="[[x]]"
attribute="[[x]]"
attribute="{{x}}">
</polymer-componentA>
<polymer-componentB id="polymer-componentB_1"
attribute="{{x}}">
</polymer-componentB>
</template>
</dom-bind>
<dom-bind id="dombind">
<template is="dom-bind">
<polymer-componentA id="polymercomponentA_2"
attribute="[[x]]"
attribute="[[x]]"
attribute="{{x}}">
</polymer-componentA>
<polymer-componentB id="polymer-componentB_2"
attribute="{{x}}">
</polymer-componentB>
</template>
</dom-bind>
I know this is not done well, but I dont find good examples about this.
Whats the right way to use two polmyer components?
Thanks in advance!
If you provide a function, Polymer calls the function once per element
instance.
When initializing a property to an object or array value, use a
function to ensure that each element gets its own copy of the value,
rather than having an object or array shared across all instances of
the element.
Source: https://www.polymer-project.org/2.0/docs/devguide/properties#configure-values
Example
static get properties() {
return {
data: {
type: Object,
value: function() { return {}; }
}
}
}

Conditional template on Polymer 1.0

I'm having trouble applying the new conditional template, in particular with the condition itself, I think.
I've got something like this:
<template is="dom-repeat" items="{{menuitems}}" as="poscol">
<template is="dom-if" if="{{index != 4}}">
<div class="positioncolum horizontal layout center wrap flex">
<span>{{index}}</span>
<template is="dom-repeat" items="{{poscol}}" as="mitem" >
<main-menu-item mitem="{{mitem}}"
order="{{mitem.TotalOrder}}"
onclick="clickMainMenuMod(index)">
</main-menu-item>
</template>
</div>
</template>
</template>
Now, if I comment the <template is="dom-if" if="{{index != 4}}"> bit it works fine, the index shows as it should.
On the fourth array are stored modules that the user has selected as non-visible, so they shouldn't appear on the main menu.
I guess there's something wrong with the if condition, but I can't guess what.
Thanks!
Try to modify your conditional template like this:
<template is="dom-if" if="{{show(index)}}">
And add this function to Polymer script:
show: function (index) {
return index != 4;
}

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>

Access core-ajax response from within nested component?

I have a core-ajax element like so:
<core-ajax id="ds" auto url="address/to/data.json" response="{{data}}"></core-ajax>
Then a list element like so:
<my-items alarms="{{data}}"></my-items>
Which iterates through the data and displays it in another template:
<template repeat="{{item in items}}">
<my-item alarm="{{item}}"></my-item>
</template>
My question is, from within "my-item", how can I access the original {{data}}? For instance, if I wanted to get a count of the total items in a javascript function?
You need to pass that information into <my-item>.
If <my-item> also published a data property, you could pass it like this:
<template repeat="{{alarm in alarms}}">
<my-item alarm="{{alarm}}" data="{{alarms}}"></my-item>
</template>
or just the stats you need:
<my-item alarm="{{alarm}}" length="{{alarms.length}}"></my-item>
Polymer('my-item', {
dataChanged: function (e) {
console.log(this.data);
}
});
that should give you the data you need

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>