I have a custom element <x-marker> with a <paper-checkbox> inside of it.
I want an checked attribute on my element to reflect down on the <paper-checkbox> but my solution doesn't seem to be the way to do it.
HTML:
<x-marker is-checked="<%= someBoolean %>"></x-marker>
Element:
<dom-module id="x-marker">
<template>
<div>
<paper-checkbox id="checkbox"></paper-checkbox>
</div>
</template>
<script>
Polymer({
is: 'x-marker',
properties: {
isChecked: String
},
listeners: {
change: 'changeHandler'
},
attached: function() {
if (this.isChecked === 'true') {
this.$.checkbox.setAttribute('checked', this.isChecked);
}
},
changeHandler: function (event, detail, sender) {
//...
}
});
</script>
</dom-module>
In Polymer 0.5 you could use checked?="{{isChecked}}", but that doesn't seem to work in 1.0 anymore. Also hard coding <paper-checkbox checked="false"> still checks the checkbox as long as the attribute is present. The value doesn't seem to matter, that's why the attribute itself has to be bound, and not its value.
I can't seem to figure this out, including treating the property as a string === 'true' instead of a boolean, and bind it straight to the <paper-checkbox>
In 1.0 you just need to use checked="{{isChecked}}". You can implement it as follows:
HTML:
<x-marker checked="{{someBoolean}}"></x-marker>
ELEMENT:
<dom-module id="x-marker">
<template>
<div>
<paper-checkbox id="checkbox" checked="{{checked}}">Check me</paper-checkbox>
</div>
</template>
<script>
Polymer({
is: 'x-marker',
properties: {
checked: {
type: Boolean,
observer: '_checkChanged'
}
},
_checkChanged: function(newValue, oldValue) {
// Do something
}
});
</script>
</dom-module>
Related
I have a component in Polymer that has an array as one of its properties.
Then I have a child component to which I'm passing this array. What I'm trying to do is to detect a change in that array from the child component.
Here is the parent component:
<dom-module id="some-test">
<template>
<paper-input label="Let's update param" on-input="updateParam"></paper-input>
<some-child param={{param}}></some-child>
</template>
<script>
Polymer({
is: 'some-test',
properties: {
param: {type: Array, value: []}
},
updateParam: function(e) {
this.param["test"] = e.currentTarget.value;
console.log("Param has been updated");
}
});
</script>
</dom-module>
And here is the child component:
<dom-module id="some-child">
<template>
<div>the child component</div>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
param: {type: Array, observer: "doSomething"},
},
doSomething: function() {
console.log("ah, we've detected a change in param");
},
});
</script>
</dom-module>
So, when I type something in the input field (in the first component), I get a consolge log of "Param has been updated". All good.
But then I would except param to be passed to the child component, which would detect the change and log "ah, we've detected a change in param".
However this doesn't seem to happen. If I use a string instead of an array, then it works as intended. But if it's an array and I update one of its values, the child component doesn't pick up the change.
This leads me to believe I am missing something as to how to bind an array. Any clues?
You can use complex observers in child components:
<dom-module id="some-child">
<template>
<div>the child component</div>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
param: {type: Array, observer: "doSomething"},
},
//Here add complex observers
observers: ['doSomething(param.*)', 'doSomething(param.splices)'],
doSomething: function() {
console.log("ah, we've detected a change in param");
},
});
</script>
</dom-module>
Ah, I finally got it working! It took a combination of the various answers. Here is the final code that works:
Parent component:
<dom-module id="some-test">
<template>
<paper-input label="Let's update param" on-input="updateParam"></paper-input>
<some-child param={{param}}></some-child>
</template>
<script>
Polymer({
is: 'some-test',
properties: {
param: {type: Array, value: []}
},
updateParam: function(e) {
this.set("param.test", e.currentTarget.value); <!-- this is the first trick -->
console.log("Param has been updated");
}
});
</script>
</dom-module>
And here is the child component:
<dom-module id="some-child">
<template>
<div>the child component</div>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
param: {type: Array},
},
observers: [
"doSomething(param.*)"
]
doSomething: function() {
console.log("ah, we've detected a change in param");
},
});
</script>
</dom-module>
So, the two things that I needed to do were:
1) Use "this.set()" to update the value of the object in the parent component
2) Use a complex observer in the child component
In AngularJS 1.5, I want to pass a binding from a component into the (multi-slot) transcluded scope - for a reference in the template (in either one specific or all of them - no either way is fine).
This is for creating a generic custom-select list
// Component
.component('mySelect', {
bind: {
collection: '<'
},
transclude:{
header: 'mySelectHeader',
item: 'mySelectItem'
},
templateUrl: 'my-select-template',
controller: function(){
.....
}
});
...
// Component template
<script id="my-select-template" type="text/ng-template">
<ol>
<li ng-transclude="header"> </li>
<li ng-transclude="item"
ng-click="$ctrl.select($item)"
ng-repeat"$item in $ctrl.collection">
</li>
</ol>
</script>
...
// Example usage
<my-select collection="[{id: 1, name: "John"}, {id: 2, name: "Erik"}, ... ]>
<my-select-head></my-select-head>
<!-- Reference to $item from ng-repeate="" in component -->
<my-select-item>{{$item.id}}: {{$item.name}}</my-select-item>
</my-select>
Is this possible from a .component()? with custom-directives for the transclusion ?
In your parent component my-select keep a variable like "selectedItem"
In your child component my-select-item, require your parent component like below
require: {
mySelect: '^mySelect'
}
And in your my-select-item component's controller, to access your parent component
$onInit = () => {
this.mySelectedItem= this.mySelect.selectedItem; // to use it inside my-select-item component.
};
select($item) {
this.mySelect.selectedItem = $item; // to change the selectedItem value stored in parent component
}
So that the selected item is now accessible from
<my-select-item>{{selectedItem.id}}: {{selectedItem.name}}</my-select-item>
I ran into this problem as well, and building upon salih's answer, I came up with a solution (disclaimer--see bottom: I don't think this is necessarily the best approach to your problem). it involves creating a stubbed out component for use in the mySelect component, as follows:
.component('item', {
require: { mySelect: '^mySelect' },
bind: { item: '<' }
})
then, tweaking your template:
<script id="my-select-template" type="text/ng-template">
<ol>
<li ng-transclude="header"> </li>
<li ng-click="$ctrl.select($item)"
ng-repeat"$item in $ctrl.collection">
<item item="$item" ng-transclude="item"></item>
</li>
</ol>
</script>
this will mean there's always an item component with the value bound to it. now, you can use it as a require in a custom component:
.component('myItemComponent', {
require: {
itemCtrl: '^item',
}
template: '<span>{{$ctrl.item.id}}: {{$ctrl.item.name}}</span>',
controller: function() {
var ctrl = this;
ctrl.$onInit = function() {
ctrl.item = ctrl.itemCtrl.item;
}
}
});
and to use it:
<my-select collection="[{id: 1, name: "John"}, {id: 2, name: "Erik"}, ... ]>
<my-select-head></my-select-head>
<my-select-item>
<my-item-component></my-item-component>
</my-select-item>
</my-select>
after I implemented this, I actually decided to change my strategy; this might work for you as well. instead of using a transclude, I passed in a formatting function, i.e.:
.component('mySelect', {
bind: {
collection: '<',
customFormat: '&?'
},
transclude:{
header: 'mySelectHeader'
},
templateUrl: 'my-select-template',
controller: function(){
var ctrl = this;
ctrl.format = function(item) {
if(ctrl.customFormat) {
return customFormat({item: item});
} else {
//default
return item;
}
}
.....
}
});
then in the template, just use:
<script id="my-select-template" type="text/ng-template">
<ol>
<li ng-transclude="header"> </li>
<li ng-click="$ctrl.select($item)"
ng-repeat"$item in $ctrl.collection"
ng-bind="::$ctrl.format($item)">
</li>
</ol>
</script>
let me know if you have any feedback or questions!
Is there a way I can create paper-card heading dynamically using some property inside custom element? Following is what I tried but didn't work. Probably this is not the way to achieve what I want:( I googled for a couple of hours but ended up with nothing!
Custom Element
<script>
(function () {
'use strict';
Polymer({
is: 'nearest-customers',
properties: {
customers: {
type: Array,
value: [],
notify: true
},
cardViewMaxRecords: {
type: Number,
notify: true
},
showFullCustomerList: {
type: Boolean,
value: false,
notify: true
},
headingContent: {
type: String,
value: 'Custom card heading'
}
},
ready: function () {
this.heading.textContent = this.headingContent
},
});
})();
</script>
HTML
<nearest-customers id="nearestCustomers" card-view-max-records="3"></nearest-customers>
...
...
...
<script type="text/javascript">
window.addEventListener('WebComponentsReady', function (e) {
var nearestCustomers = document.querySelector("#nearestCustomers");
nearestCustomers.headingContent= "<a href='someurl'><iron-icon icon='fa:arrow-left'></iron-icon></a> This is a new content";
}
</script>
My objective is to put an iron-icon before the heading text and the icon can be used as a link to somewhere.
Thanks in advance
I'm sure there's a better way, but I just added the styles and structure:
<div class="header paper-card">
<div class="title-text paper-card">
<iron-icon icon="book"></iron-icon> Reading List
</div>
</div>
I'm trying to re-compute and render a value within a Polymer 1.0 template. However, I'm trying to do this without binding to any properties.
In case it matters, the use case is for a translation mechanism that uses a string key to find the translated value. When the 'translations' value changes, the translate() call needs to be re-computed.
The component definition is as follows :
<dom-module id="my-component">
<template>
<style></style>
<p><span>[[translate("SOME_STRING")]]</span></p>
</template>
<script>
var MyComponent = Polymer({
is: "my-component",
properties: {
translations: {
type: Object,
notify: true,
value: {
"SOME_STRING": "Some String"
}
}
},
translate: function (key) {
if (this.translations.hasOwnProperty(key)) {
return this.translations[key];
}
}
});
</script>
</dom-module>
I can get the refresh to work by adding the translations property to the translate() call as follows :
<p><span>[[translate("SOME_STRING", translations)]]</span></p>
However, what I would like to do is re-compute/refresh without having to put the translations property as a second parameter in every call (there's other reasons too).
Basically, when the translations object updates with different locale translations, I'd like the translate("SOME_STRING") to be re-computed.
Is this possible? Is there any way to re-render the template or even just re-render the entire component manually? How? If not, what is the simplest way to get the computed value or template re-rendered without a property in the binding?
How about that????
<dom-module id="my-component">
<template>
<style></style>
<p><span>[[str]]</span></p>
</template>
<script>
var MyComponent = Polymer({
is: "my-component",
properties: {
translations: {
type: Object,
notify: true,
value: {
"SOME_STRING": "Some String"
},
observer: '_Changed'
},
str: {
type: String,
value: "hello"
}
},
_Changed: function(){
this.set("str",this.translate(this.str));
},
translate: function (key) {
if (this.translations.hasOwnProperty(key)) {
return this.translations[key];
}
}
});
</script>
I want to set label/s of paper-checkbox elements through a custom element I have created.
This is how I am calling my custom element with the value set to a property called optionLabel which I want to display when checkbox renders on the screen.
<check-list optionLabel="My first checkbox"></check-list>
My custom element check-list looks like this:
<dom-module id="check-list">
<template>
<style>
:host {
display: block;
}
</style>
<paper-checkbox on-change="_checkChanged">{{optionLabel}}</paper-checkbox>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'check-list',
properties: {
optionLabel: {
type: String,
notify: true
}
},
_checkChanged: function (e) {
alert("State changed");
}
});
})();
</script>
</dom-module>
My goal is to reuse my custom element inside a dom-repeat layout and set values according to the requirement.
What is the correct way of doing this?
According to the documentation camelCase properties are "accessed" from outside the element like camel-case. The documentation states the following:
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. The same mappings happen in reverse when converting property names to attribute names.
In other words, your code should have worked if you did the following instead:
<check-list option-label="My first checkbox"></check-list>
I got it to work! The variable (property) I was using previously was optionLabel, which did not work. Don't know what is the reason but when I changed it to optionlabel, i.e. all lowercase, it worked fine!
Not sure if above is the true solution to the problem I faced but it is working for me now :)
However, it will still be very helpful for many beginners like me if somebody please explain why optionLabel did not work.
So my code now changes to this
Custom element:
<dom-module id="check-list">
<template>
<style>
:host {
display: block;
}
</style>
<paper-checkbox on-change="_checkChanged">{{optionlabel}}</paper-checkbox>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'check-list',
properties: {
optionlabel: {
type: String,
notify: true
}
},
_checkChanged: function (e) {
if (e.target.checked) {
this.optionlabel = "Good to see you agree";
this.$.btnsubmit.disabled = false;
} else {
this.optionlabel = "Please agree to proceed";
this.$.btnsubmit.disabled = true;
}
}
});
})();
</script>
</dom-module>
And the call looks like:
<check-list optionlabel="My first checkbox"></check-list>