How to reflect change from child to parent in Polymer - polymer

The current code is below. I have element-value in the main file. This value is passed to the child elements app-element and from there to app-element-add.
The value changes in app-element-add. But I cannot get the value reflected in the main element.
The observers never get invoked.
main.html
<app-element element-value = {{ elementValue }}></app-element>
Polymer({
is: 'app-main-element',
properties: {
elementValue: {
type:Array,
notify:true,
observer:'listUpdated'
}
});
app-element.html
<app-element-add element-value = {{ elementValue }}></app-element-add>
Polymer({
is: 'app-element',
properties: {
elementValue: {
type:Array,
notify:true,
observer:'listUpdated'
}
});
app-element-add.html
Polymer({
is: 'app-element-add',
properties: {
elementValue: {
type:Array,
notify:true,
reflectToAttribute:true
}
});
Any hints on how to reflect changes in app-element-add in app-main-element. Thanks.

You don't need to use reflectToAttribute here. The only option required here is notify. However, your current code works:
HTMLImports.whenReady(_ => {
"use strict";
Polymer({
is: 'app-main-element',
properties : {
elementValue: {
type: Array,
notify: true,
observer: 'listUpdated',
value: _ => [100,200,300]
}
},
listUpdated: function() {
console.log('[app-main-element] list updated');
},
ready: function() {
console.log('[app-main-element] ready');
}
});
Polymer({
is: 'app-element',
properties : {
elementValue: {
type: Array,
notify: true,
observer: 'listUpdated'
}
},
listUpdated: function() {
console.log('[app-element] list updated');
},
ready: function() {
console.log('[app-element] ready');
}
});
Polymer({
is: 'app-element-add',
properties : {
elementValue: {
type: Array,
notify: true
}
},
ready: function() {
console.log('[app-element-add] ready (will set elementValue in 1000ms)');
this.async(_ => {
console.log('[app-element-add] updating elementValue');
this.elementValue = [1,2,3];
}, 1000);
}
});
});
<head>
<base href="https://polygit.org/polymer+1.11.0/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<app-main-element></app-main-element>
<dom-module id="app-main-element">
<template>
<app-element element-value={{elementValue}}></app-element>
<div>app-main-element.elementValue = [[elementValue]]</div>
</template>
</dom-module>
<dom-module id="app-element">
<template>
<app-element-add element-value={{elementValue}}></app-element-add>
<div>app-element.elementValue = [[elementValue]]</div>
</template>
</dom-module>
<dom-module id="app-element-add">
<template>
<div>app-element-add.elementValue = [[elementValue]]</div>
</template>
</dom-module>
</body>
codepen

Related

Polymer sending/receiving events among sibling elements

Assume I have two sibling elements, and <element-b> fires an event. How does <element-a> listen to the event without having to add imperative code to the parent?
<dom-module id="parent-element">
<element-a></element-a>
<element-b></element-b>
</dom-module>
where <element-a> and <element-b> are:
<dom-module id="element-a">
<template>
<style include="shared-styles">
</style>
</template>
<script>
Polymer({
is: 'element-a',
listeners: {
'element-b': 'handleEvent',
},
ready: function() {
console.log('fired from element a')
this.fire('element-a', {employee: ''});
},
handleEvent: function (e) {
console.log('received element b event', e)
}
});
</script>
</dom-module>
<dom-module id="element-b">
<template>
<style include="shared-styles">
</style>
</template>
<script>
Polymer({
is: 'element-b',
listeners: {
'element-a': 'handleEvent',
},
ready: function() {
console.log('fired from element b')
this.fire('element-b', {employee: ''});
},
handleEvent: function (e) {
console.log('received element a event', e)
}
});
</script>
Thanks!
You could use <iron-signals> for that.
Add an <iron-signals> listener in one element:
// element-b
<iron-signals on-iron-signal-element-a="_onSignalElementA"></iron-signals>
_onSignalElementA: function(e) {
const newDate = e.detail;
...
}
...and fire an iron-signal event (with data) in the other:
// element-a
this.fire('iron-signal', {name: 'element-a', data: new Date()});
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo'
});
Polymer({
is: 'element-a',
_onClick: function() {
this.fire('iron-signal', {name: 'element-a', data: new Date()});
}
});
Polymer({
is: 'element-b',
_onSignalElementA: function(e) {
this._message = `b received: ${e.detail}`;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.11.0/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="iron-signals/iron-signals.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<element-a></element-a>
<element-b></element-b>
</template>
</dom-module>
<dom-module id="element-a">
<template>
<button on-tap="_onClick">Fire event</button>
</template>
</dom-module>
<dom-module id="element-b">
<template>
<iron-signals on-iron-signal-element-a="_onSignalElementA"></iron-signals>
<div>[[_message]]</div>
</template>
</dom-module>
</body>
codepen

Polymer. Update view after model changed

This is a common question in Polymer about updating view when model was updated (answer is to use this.set or this.push Polymer methods).
But when I have two elements:
First element:
properties:{
items: {
type: Array
}
},
someFunction: {
this.push('items', {'Name': 'something'});
}
Second Element has property which is bound to 'items' from first element
ready: function(){
this.items = firstElement.items;
}
I would like second element 'items' to be updated when 'items' updated on firstElement. I can't manually notify secondElement, because of app restrictions.
So for now I see (in devTools) that model of second element is correct ('items' contains objects), but view isn't updated.
How to update it?
You need to set notity on the property of the first element to receive changes outside of the element itself
properties:{
items: {
type: Array,
notify: true
}
},
then, in secondElement you can
<firstElement items="{{items}}" />
Let Polymer's two-way binding handle the notifications for you.
Consider two elements, x-foo and x-bar, in a container element, x-app.
In x-foo, declare items with notify:true so that its changes would be propagated upward.
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() { return []; },
notify: true
}
}
});
In x-app, bind x-foo's items to x-bar's.
<dom-module id="x-app">
<template>
<x-foo items="{{items}}"></x-foo>
<x-bar items="[[items]]"></x-bar>
</template>
<script>
Polymer({ is: 'x-app' });
</script>
</dom-module>
Now, x-bar would be notified of all changes to the items array (when an item is added/removed).
HTMLImports.whenReady(() => {
"use strict";
Polymer({
is: 'x-app'
});
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() { return []; },
notify: true
}
},
_addItem: function() {
this.push('items', {name: 'item' + this.items.length});
}
});
Polymer({
is: 'x-bar',
properties: {
items: {
type: Array
}
}
});
});
<head>
<base href="https://polygit.org/polymer+1.6.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-app></x-app>
<dom-module id="x-app">
<template>
<x-foo items="{{items}}"></x-foo>
<x-bar items="[[items]]"></x-bar>
</template>
</dom-module>
<dom-module id="x-foo">
<template>
<h2><code>x-foo</code></h2>
<button on-tap="_addItem">Add item</button>
</template>
</dom-module>
<dom-module id="x-bar">
<template>
<h2><code>x-bar</code></h2>
<ul>
<template is="dom-repeat" items="[[items]]">
<li>[[item.name]]</li>
</template>
</ul>
</template>
</dom-module>
</body>
codepen

Adding Polymer component attributes dynamically

I have a polymer component included inside another polymer component like this
<dom-module id="custom-component2">
<template>
<custom-component1 id="component1" type="abc" config="xyz"></custom-component1>
</template>
</dom-module>
<script>
Polymer({
is: 'custom-component2',
properties: {
},
ready: function() {
},
init: function() {
}
});
</script>
Is there anyway I can add the attributes such as type, config for my "custom-component1" dynamically like -
<dom-module id="custom-component2">
<template>
<custom-component1 id="component1"></custom-component1>
</template>
</dom-module>
<script>
Polymer({
is: 'custom-component2',
properties: {
},
ready: function() {
self.$.component1.type = "abc";
self.$.component1.config = "xyz";
}
});
</script>
or can I pass these options as a whole as an object?
Could someone help me on this please?
This should work:
<dom-module id="custom-component2">
<template>
<custom-component1 id="component1"></custom-component1>
</template>
</dom-module>
<script>
Polymer({
is: 'custom-component2',
properties: {
},
attached: function() {
this.$.component1.type = "abc";
this.$.component1.config = "xyz";
}
});
</script>
Using data-binding with an object:
<dom-module id="custom-component2">
<template>
<custom-component1 id="component1" data={{_data}}></custom-component1>
</template>
</dom-module>
<script>
Polymer({
is: 'custom-component2',
properties: {
_data:{
type: Object,
value: function(){
return {type:"abc", config:"xyz"};
}
},
},
});
</script>

How do I set an element's height with Polymer?

I have created a Polymer behavior that is supposed to take a selected element's width and set its height to that width on "ready" and when the window is resized. I've done the "get the width" part but setting the height does not work. Here's the code:
<link rel="import" href="../bower_components/polymer/polymer.html">
<script>
SquareBehavior = {
properties: {
victim: Object
},
listeners: {
'resize': 'squareIt' // not working
},
squareIt: function() {
this.victim = this.$$('.round');
console.log(this.victim.offsetWidth); // this works fine
// what do I add here?
},
ready: function() {
this.squareIt(); //works
}
};
</script>
You can use iron-resizable-behavior.
https://github.com/polymerelements/iron-resizable-behavior
<html>
/*...*/
<link rel="import" href="../../bower_components/iron-resizable-behavior/iron-resizable-behavior.html">
/*...*/
Polymer({
/*...*/
listeners: {
'iron-resize': "_resizeHandler"
},
You might have to declare style property and set the reflect to attribute to true
<link rel="import" href="../bower_components/polymer/polymer.html">
<script>
SquareBehavior = {
properties: {
victim: Object
style: {
type: String,
reflectToAttribute: true
}
},
listeners: {
'resize': 'squareIt' // not working
},
squareIt: function() {
this.victim = this.$$('.round');
console.log(this.victim.offsetWidth); // this works fine
// what do I add here?
this.style = "{height: " + this.victim.offsetWidth + " }"
},
ready: function() {
this.squareIt(); //works
}
};
</script>

Extending native HTML Elements with Polymer

Hi i'd like to extend from a native HTML Element with Polymer for creating a custom web component.
My polymer ready-callback is getting called, when i'm not extending. As soon as i extend, nothing gets called anymore. Though the shadow DOM for the element is being created...
Here is my code for the usage:
<!DOCTYPE html>
<html>
<head>
<title>Custom Component Usage</title>
<script src="bower_components/platform/platform.js"></script>
<link rel="import" href="elements/extended-item.html">
</head>
<body>
<extended-item>I was extended from div!</extended-item>
</body>
</html>
Custom Element, which extends div:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="extended-item" extends="div">
<template>
<content></content>
</template>
</polymer-element>
<script>
Polymer('extended-item',
{
created: function ()
{
console.log('EXTENDED ITEM IS CREATED!');
},
ready: function ()
{
console.log('EXTENDED ITEM IS READY!');
},
attached: function ()
{
console.log('EXTENDED ITEM IS ATTACHED!');
},
domReady: function ()
{
console.log('EXTENDED ITEMS DOM IS READY!');
},
detached: function ()
{
console.log('EXTENDED ITEM IS DETACHED!');
},
attributeChanged: function (attrName, oldVal, newVal)
{
//var newVal = this.getAttribute(attrName);
console.log(attrName, 'old: ' + oldVal, 'new:', newVal);
}
});
</script>
Any idea?
thanks in advance,
Rob
This works properly if, instead of referring to the element as <extended-item>, you instead use <div is="extended-item">.
Here's an example (jsbin):
<polymer-element name="extended-item" extends="div">
<template>
<p>This is part of the template.</p>
<content></content>
</template>
<script>
Polymer('extended-item', {
created: function() {
console.log('EXTENDED ITEM IS CREATED!');
},
ready: function() {
console.log('EXTENDED ITEM IS READY!');
},
attached: function() {
console.log('EXTENDED ITEM IS ATTACHED!');
},
domReady: function() {
console.log('EXTENDED ITEMS DOM IS READY!');
},
detached: function() {
console.log('EXTENDED ITEM IS DETACHED!');
},
attributeChanged: function (attrName, oldVal, newVal)
{
//var newVal = this.getAttribute(attrName);
console.log(attrName, 'old: ' + oldVal, 'new:', newVal);
}
});
</script>
</polymer-element>
<div is="extended-item">I was extended from div!</div>
EDIT: As pointed out in this comments, this is expected behavior, documented in the Custom Elements spec.