Polymer 1.0 - Issue selecting dynamic element by id - javascript

I am trying to select an element by id that was created dynamically:
<dom-module id="filter-box">
<style>
:host {
display: block;
}
</style>
<template>
<h4 id="title">{{title}}</h4>
<paper-checkbox id="test">test</paper-checkbox><br>
<template is="dom-repeat" items="{{filters}}">
<paper-checkbox id="{{item}}">{{item}}</paper-checkbox><br>
</template>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'filter-box',
properties: {
filters: {
type: Array,
notify: true,
},
title: {
type: String
}
},
ready: function() {
this.filters = [
'Commercial',
'Enterprise'
];
},
isSelected: function(filter) {
return this.$$[filter].checked;
}
});
})();
</script>
When isSelected("something") is called I get:
"Uncaught TypeError: Cannot read property 'checked' of undefined"
I can select the title via this.$.title, however I can not select the dynamic elements this way or using this.$$ as suggested here.

According the reference you provided you are supposed to call this.$$(selector) with parentheses as in a function call (and not with brackets). So replace your code with this:
return this.$$('#'+filter).checked;
Note that you may also need to prepend the id selector with #.

Related

CheckBox disabled attribute bound to Polymer Property's sub-property

I am trying to bind a Polymer property to the disabled attribute of a CheckBox, but the CheckBox only get disabled/enabled at the beginning.
I have to mention that the property I am binding to is a sub-property of a Polymer property.
Component's code:
<link rel="import" href="../polymer/polymer.html"/>
<dom-module id="checkbox-disabled-example">
<template>
<button
on-click="onClick" >
Toggle Disabled Enabled </button>
<template is="dom-repeat" items="{{elementsObject.items}}" as="item">
<input
type="checkbox"
disabled$="{{item.disabled}}" >
{{item.name}} <br>
</template>
</template>
<script>
Polymer({
is: 'checkbox-disabled-example',
properties:{
elementsObject:{
type: Object,
value: {}
},
},
onClick: function(){
this.elementsObject.items.forEach(
function(item, index, array){
item.disabled = !item.disabled;
}
);
}
});
</script>
</dom-module>
index.html code:
<body>
<template id="just-for-demo" is="dom-bind" >
<checkbox-disabled-example elements-object={{ckData}} >
</checkbox-disabled-example>
</template>
<script>
window.addEventListener('WebComponentsReady', function() {
var template = document.querySelector('template[is=dom-bind]');
var data =
{
items:
[
{ disabled: true, name:"Element 01"},
{ disabled: false, name:"Element 02"},
{ disabled: false, name:"Element 03"},
{ disabled: false, name:"Element 04"},
{ disabled: false, name:"Element 05"},
{ disabled: true, name:"Element 06"}
]
};
template.ckData = data;
});
</script>
</body>
As you can see I create several Check Boxes, each one of them bound to the disabled property of its item.
When I click on the button I toggle the disabled value of each item, however the Check boxes states don't change.
What am I doing wrong?
Changes that imperatively mutate an object or array are not observable.
If you have a simple property like:
this.name = 'Jane';
Polymer will automatically create a setter and it will automatically pick up any changes on that property.
However, changes on an object subproperty or an array item will not work:
this.address.street = 'Elm Street';
The setter on address will not be called and the change will not be detected.
Polymer provides specific methods for making observable changes to subproperties and arrays, in the example above you would need to call:
this.set('address.street', 'Elm Street');
The address.street part is called path. It's a string that identifies a property or subproperty relative to a scope. In most cases, the scope is a host element.
If you are creating a path to an array item, you will need to pass the index:
this.set('items.5', 'Changed index 5');
In your specific example, a simple change to the click event handler would be enough:
onClick: function() {
var that = this;
this.elementsObject.items.forEach(function(item, index, array) {
that.set('elementsObject.items.'+index+'.disabled', !item.disabled);
});
}
Or you can use the cool new ES6 arrow function syntax to make it more readable and remove the ugly that = this:
onClick: function() {
this.elementsObject.items.forEach((item, index, array) => {
this.set('elementsObject.items.'+index+'.disabled', !item.disabled);
});
}

Polymer: Vers simple data binding doesn't work in the second element

I am working on this issue for 6 hours now and I seem to be unable to see it.
So here is the snippet from the index.html:
<flat-data-array availableModes="{{modes}}" id="dataArray"></flat-data-array>
<flat-strip-view availableModes="{{modes}}" id="flatViewer"></flat-strip-view>
the dataArray (which works always fine):
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="flat-data-array">
<script>
(function() {
'use strict';
Polymer({
is: 'flat-data-array',
properties: {
strips: {
type: Array,
notify: true,
observe: '_stripsChanged'
},
availableModes: {
type: Number,
notify: true,
observe: '_modesChanged'
},
socket: {
type: Object
}
}
,
_stripsChanged: function(newVal, oldVal) {
this.fire('flat-strip-array-changed',{ newValue: newVal, oldValalue: oldVal});
},
_modesChanged: function(newVal, oldVal) {
this.fire('flat-strip-mode-changed',{ newValue: newVal, oldValalue: oldVal});
alert("Changed");
},
ready: function() {
this.socket = io.connect('http://192.168.0.101:3000');
socket.on('flatCommand', function(data) {
console.log(data);
});
this.availableModes=15;
/*[
{
modeID: 65,
letter: "A",
name: "Singler Color"
}
];*/
}
});
})();
</script>
</dom-module>
and now the problem:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../elements/flat-list/flat-list.html">
<dom-module id="flat-strip-view">
<template>
<style>
:host {
display: block;
}
</style>
<flat-list
strips="{{strips}}"
id="flatList"
>
</flat-list>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'flat-strip-view',
properties: {
strips: {
type: Array,
notify: true,
observer: '_flatStripChanged'
},
availableModes: {
type: Number,
notify: false,
observer: '_flatModeChanged'
}
}
,
_flatModeChanged: function(newVal, oldVal) {
this.fire('flat-strip-view-mode-changed',{ newValue: newVal, oldValalue: oldVal});
alert("Event");
},
_flatStripChanged(newVal, oldVal) {
this.fire('flat-strip-view-array-changed',{ newValue: newVal, oldValalue: oldVal});
}
});
})();
</script>
</dom-module>
due to the definition in the index.html I'd expect the availableModes to be the same in both elements. But if i type:
documtent.getElementById('dataArray').availableModes
I get 15 (perfectly ok), but when I type
document.getElementById('flatViewer').availableModes it says undefined
Oddly enough, had another definition in the same manner before (infact I only removed it to track down the problem) and that worked and passed the values down to the last element in the cain. I just can't see any difference.
<aiur-data-array strips="{{mystrips}}" availableModes="{{modes}}" id="dataArray"></aiur-data-array>
<aiur-strip-view availableModes="{{modes}}" strips="{{mystrips}}" id="aiurViewer"></aiur-strip-view>
That worked for "strips" in any direction with any element...
Change the attribute availableModes to available-modes.
When mapping attribute names to property names:
Attribute names are converted to lowercase property names. For example, the attribute firstName maps to firstname.
Attribute names with dashes are converted to camelCase property names by capitalizing the character following each dash, then removing the dashes. For example, the attribute first-name maps to firstName.
Souce: https://www.polymer-project.org/1.0/docs/devguide/properties.html#property-name-mapping

Polymer dom-repeat how to notify array updated

So I have this Polymer element with dom-repeat. It binds correctly. However, when the array is modified, it doesn't reflect back to DOM. Nothing changed when I click on the button.
<dom-module id="my-element">
<template>
<template is="dom-repeat" id="allRules" items="{{allRules}}">
<span class="tag" rule-enabled$={{item.enabled}}>{{item.name}}</span>
</template>
<button on-click="click">Click me</button>
</template>
</dom-module>
<script>
Polymer({
is: "my-element",
properties: {
allRules: {
type: Array,
notify: true
}
},
click: function() {
this.allRules[0].name = "three";
},
ready: function() {
this.allRules = [
{
name: "one",
enabled: true
}, {
name: "two",
enabled: false
}
]
},
setBind: function(bind) {
this.bind = bind;
}
});
</script>
Is there a method like notifyArrayUpdated to tell the DOM to update binding data?
When you change a subproperty inside an array, you need to do
this.set('allRules.0.name', 'three');
Check out array binding for details.
Here is a plunker for it.

Shared behavior state across custom elements

I have these two custom Polymer Elements (Polymer 1.0.3):
Displays text to be translated.
Displays button to trigger loading of translation.
I also have a Behavior that holds the translations (json object) and contains all the functions that make translation possible.
This is what I expect to happen:
Click the button in Element 2
Translations load into the Behavior
Language selection is set in the Behavior
Text in Element 1 is updated with the translated equivalent
Steps 1 - 3 happen, but 4 doesn't. The text is never updated. I can get it to work if Elements 1 & 2 are combined as the same element, but not if they're separate (any they need to be separate).
If you're wondering about the "kick" property, it's something I learned from Polymer 0.5. It got things working when the two elements were combined, so I'm thinking it'll be be necessary when the elements are separate.
Any idea how I can make this happen? I'm open to alternative paradigms.
Code
This is roughly how my code is laid out. I also made a plunker with a single-page test case.
index.html
<!doctype html>
<html>
<head>
<script src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html">
<link rel="import" href="behavior.html">
<link rel="import" href="element1.html">
<link rel="import" href="element2.html">
</head>
<body>
<my-element></my-element>
<another-element></another-element>
</body>
</html>
Element 1
<dom-module id="my-element">
<template>
<p>{{localize(label, kick)}}</p>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
behaviors: [
behavior
],
properties: {
label: {
type: String,
value: 'original'
}
}
});
</script>
Element 2
<dom-module id="another-element">
<template>
<button on-click="buttonClicked">load</button>
</template>
</dom-module>
<script>
Polymer({
is: 'another-element',
behaviors: [
behavior
],
buttonClicked: function() {
this.registerTranslation('en', {
original: 'changed'
})
this.selectLanguage('en');
}
});
</script>
Behavior
<script>
var behavior = {
properties: {
kick: {
type: Number,
value: 0
},
language: {
type: String,
value: 'fun'
},
translations: {
type: Object,
value: function() {
return {};
}
}
},
localize: function(key, i) {
if (this.translations[this.language] && this.translations[this.language][key]) {
return this.translations[this.language][key];
}
return key;
},
registerTranslation: function(translationKey, translationSet) {
this.translations[translationKey] = translationSet;
},
selectLanguage: function(newLanguage) {
this.language = newLanguage;
this.set('kick', this.kick + 1);
}
};
</script>
First, although the notion is to have behavior be a conduit for shared data between instances, as written, each instance will have it's own copy of the translations object and the kick property.
Second, even if that data was privatized so it could be shared, the kick binding made via localize(label, kick) is in a different scope from the expression that modifies kick (i.e. this.set('kick', this.kick + 1); [{sic} this could simply be this.kick++;]).
To notify N instances of a change in shared data, one must keep track of those instances. A good way to do this is by attaching event listeners. Another way is simply keeping a list.
Here is an example implementation of your design:
<script>
(function() {
var instances = [];
var translationDb = {};
var language = '';
window.behavior = {
properties: {
l10n: {
value: 0
}
},
attached: function() {
instances.push(this);
},
detached: function() {
this.arrayDelete(instances, this);
},
_broadcast: function() {
instances.forEach(function(i) {
i.l10n++;
});
},
localize: function(key, i) {
if (translationDb[language] && translationDb[language][key]) {
return translationDb[language][key];
}
return key;
},
registerTranslation: function(translationKey, translationSet) {
translationDb[translationKey] = translationSet;
},
selectLanguage: function(newLanguage) {
language = newLanguage;
this._broadcast();
}
};
})();
</script>
<dom-module id="my-element">
<template>
<p>{{localize(label, l10n)}}</p>
</template>
<script>
Polymer({
behaviors: [
behavior
],
properties: {
label: {
type: String,
value: 'original'
}
}
});
</script>
</dom-module>
<dom-module id="another-element">
<template>
<button on-tap="buttonClicked">load</button>
</template>
<script>
Polymer({
behaviors: [
behavior
],
buttonClicked: function() {
this.registerTranslation('en', {
original: 'changed'
});
this.selectLanguage('en');
}
});
</script>
</dom-module>

Understanding Polymer events and data-bindings with nested elements?

I have three nested polymer elements. Each of them has default values, which may be overridden with published value.
But i can't do it.
On create event does not exist published value. On ready event child element has already ready and not updated.
I made example js-bin.
In reality, each element in the separate file. It is example.
Object.assign - es6 method for merging objects (use es6-shim).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/es6-shim/0.18.0/es6-shim.js"></script>
<script src="http://www.polymer-project.org/components/platform/platform.js"></script>
<link href="http://www.polymer-project.org/components/polymer/polymer.html" rel="import">
</head>
<body unresolved>
<polymer-element name="my-field" attributes="ask">
<template>
<span><content></content> Message: {{message}}</span>
</template>
<script>
(function() {
Polymer('my-field', {
defaultAsk: { message: "Hello from Field!" },
created: function() {
console.log('created-field', this.ask, this.message);
Object.assign(this, this.defaultAsk);
},
ready: function() {
console.log('ready-field', this.ask, this.message);
Object.assign(this, this.ask);
}
});
})();
</script>
</polymer-element>
<polymer-element name="my-shelf" attributes="ask">
<template>
<my-field ask="{{field}}"><content></content></my-field>
</template>
<script>
(function() {
Polymer('my-shelf', {
defaultAsk: { field: { message: "Hello from Shelf!" } },
created: function() {
console.log('created-shelf', this.ask, this.field);
Object.assign(this, this.defaultAsk);
},
ready: function() {
console.log('ready-shelf', this.ask, this.field);
Object.assign(this, this.ask);
}
});
})();
</script>
</polymer-element>
<polymer-element name="my-cabinet">
<template>
<my-shelf ask="{{shelf}}"><content></content></my-shelf>
</template>
<script>
(function() {
Polymer('my-cabinet', {
ask: { shelf: { field: { message: "Hello from outside!" } } },
defaultAsk: { shelf: { field: { message: "Hello from Cabinet!" } } },
created: function() {
console.log('created-cabinet', this.ask, this.shelf);
Object.assign(this, this.defaultAsk);
},
ready: function() {
console.log('ready-cabinet', this.ask, this.shelf);
Object.assign(this, this.ask);
}
});
})();
</script>
</polymer-element>
<my-cabinet>Yo!</my-cabinet>
</body>
</html>
All will working as I want, if use only one object through many nested elements.
Example on js-bin
All my element extended of other one (my-elem) only for remove doubled code and make my example more readable. ask - binding data (from my-cabinet to my-field). Click - changing data.
Unfortunately, on jsbin wery uneasy console for view fired messages.
I made small schema of chain events with nested elements. Maybe, it will help someone.
p.s. Don't kick me for my bad english.

Categories

Resources