Polymer: Transfer data between sibling pages(elements) - polymer

I need help by transferring data between two sibling elements of the polymer app. I can't make one of them to be the parent so that I can use dispatchEvent for the transfer of these elements.
Is there any way to transfer data between two sibling elements in polymer?

Of course, for example you can use DataBinding:
<some-element param="{{value}}"></some-element>
<another-element param="{{value}}"></another-element>
In these elements you can change this "value" param and it will be changed in another one:
<script>
class ...
changeParam(){
this.param="newValue";
}
</script>
Another way is using events. But you don't need to dispatch it. For example:
class ....
static get properties(){
return {
prop:{
type: Object,
value: {},
notify: true // (*)
}
}
In line with * you can see notify property. It fires event "prop-changed" when, as you could figure out, prop changes. So you can addEventListener for this event.
Note: somePropertyWhichCanBeChanged changes to some-property-which-can-be-changed-changed event.

My code:
<my-view1 name="view1" param="{{value}}"></my-view1>
<my-view2 name="view2" param="{{value}}"></my-view2>
VIEW1:
static get properties(){
return {
eventValue: {
type: String,
value: "Mirza",
notify: true
}
}
}
changeParam(){
this.param = "Azrim";
this.eventValue = "Mirzoni";
console.log("Param in view1: " + this.param);
console.log("EventValue in view1: " + this.eventValue);
}
VIEW2:
static get is() { return 'my-view2'; }
seeParam(){
console.log("Param in view2: " + this.param);
this.addEventListener('event-value-changed', function(e){
console.log("Receiving value...");
});
}

Related

Bind Polymer 2.x event handler to a global function?

I have some Polymer elements that are not inside an element/app, and I can't figure out how to attach their event handlers, such as on-click, to a global javascript function.
For example, let's say my code looks something like this
<head>
// Import stuff...
function login(){
// Do stuff
}
</head>
<body unresolved>
<dom-if id="signInItem">
<template>
<paper-button on-tap="login"><iron-icon icon="icons:account-circle"></iron-icon> Log in</paper-button>
</template>
</dom-if>
</body>
This won't work, because on-tap expects to bind to a property of an enclosing element (I guess). What are my options?
give some id for paper-button
<paper-button id="button"></paper-button>
in javascript you can add eventlistener as shown below
this.$.button.addEventListener('click', e => {
console.log("clicked");
// write your code
});
or you can write your code in seperate function
ready() {
super.ready();
this.$.button.addEventListener('click', e => this.handleClick(e));
}
handleClick(e) {
console.log(e);
}
If you like to fire a function not in polymer you may call;
this.dispatchEvent(new CustomEvent('login', { bubbles: true, composed: true, detail: true }));
This will fire your login listen in global js (ie. main.js) as below;
addEventListener('login', function (e) {
// Do something for login
})
EDIT: I think you also need to change dom-if to dom-bind in order to bind this code as its at root level
You can always forward a call to a local class member function (MyElement.login) to a global namespace:
/* 1 */ class MyElement extends Polymer.Element {
/* 2 */ // ...
/* 3 */
/* 4 */ login: (() => (evt) (MyGlobalNamespace || window).login.bind(this))()
/* 5 */ }
You can even omit the surrounding closure function - if you're not using this within the implementation of login -, simplifying line 4 to: login: (MyGlobalNamespace || window).login

how to force a Polymer.Element extended class to execute its lifecycle without attaching it to the dom?

Consider this element (minimal for the purpose of the question) :
class MyCountDown extends Polymer.Element
{
static get is () { return 'my-count-down'; }
static get properties ()
{
return {
time: { /* time in seconds */
type: Number,
observer: '_startCountDown'
},
remains: Number
}
}
_startCountDown ()
{
this.remains = this.time;
this.tickInterval = window.setInterval(() => {
this.remains--;
if (this.remains == 0) {
console.log('countdown!');
this._stopCountDown();
}
}, 1000);
}
_stopCountDown () {
if (this.tickInterval) {
window.clearInterval(this.tickInterval);
}
}
}
customElements.define(MyCountDown.is, MyCountDown);
If I get one instance and set the property time,
let MyCountDown = customElements.get('my-count-down');
let cd = new MyCountDown();
cd.time = 5;
the property time changes but the observer and the _startCountDown() function is not called. I believe Polymer is waiting for the Instance to be attached to the DOM because in fact when I appendChild() this element to the document the count down starts and after 5 seconds the console logs 'countdown!' as expected.
My goal is to execute this lifecycle without attaching anything to the document because the instances of MyCountDown are not always attached to the view but/and they need to be live-code between the different components of my web application.
One solution is to attach the new MyCountDown instances to an hidden element of the dom to force the Polymer lifecycle but I think this is not so intuitive.
I don't know the exact place to call, but the problem you have is that the property assessors are not in place.
I think you might get a clue from this talk https://www.youtube.com/watch?v=assSM3rlvZ8 at google i/o
call this._enableProperties() in a constructor callback?

Why use function wrap for Polymer property value of type object?

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.
this is from the official polymer document my question here is why to not to share this default value across multiple instance as this default value will only be called once during initialization ??
<dom-module id="title-element">
<template>
<h1 style$="background-color: {{backgroundColor}}">{{config.title}}</h1>
</template>
<script>
Polymer({
is: 'title-element',
properties: {
config: {
type: Object,
notify: true,
value: {
title: 'This is my element',
}
},
backgroundColor: String,
},
ready: function () {
this.addEventListener('config-changed', function () {
console.log('config had changed');
this.querySelector('h1').style.backgroundColor = 'blue';
})
}
})
</script>
</dom-module>
<title-element background-color="yellow"></title-element>
<title-element background-color="green"></title-element>
in the above example i tried to change the value of config.title by selecting that element in chrome console and change it once using $0.config = {"title":"any other"} and also using notifyPath and as expected it changed only in the selected element not all instances
what is the purpose of using function wrap then ?
So that every element gets it own copy instead of sharing it.
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.
Here's the link to documentation
Here's a simple test case to depict the same.
<link rel="import" href="http://polygit.org/components/polymer/polymer.html">
<dom-module id="shared-object">
<template>
<style></style>
<div on-tap='changeValue'>Shared Object: {{shared.key}}</div>
<div on-tap='changeValue'>Not Shared Object: {{notShared.key}}</div>
</template>
</dom-module>
<script>
Polymer({
is: 'shared-object',
properties: {
shared: {
type: Object,
value: {
key: 'value'
}
},
notShared: {
type: Object,
value: function() {
return {
key: 'value'
}
}
}
},
changeValue: function() {
this.set('shared.key', 'value1');
this.set('notShared.key', 'value1');
}
})
</script>
Instance one
<br>
<shared-object id='obj1'></shared-object>
<br>
<br>Instance two
<br>
<shared-object id='obj2'></shared-object>
<script>
document.querySelector('shared-object').addEventListener('tap', function() {
console.log('First instance:\nshared value:', document.querySelector('#obj1').shared, '\nnot shared value:', document.querySelector('#obj1').notShared);
console.log('second instance:\nshared value:', document.querySelector('#obj2').shared, '\nnot shared value:', document.querySelector('#obj2').notShared);
})
</script>
After you tap on any of the value you'll notice that even though the display values are correct for all the cases but in console shared object has same value for both the instances, whereas notSharedObject has different value even in console.

Sammy.js : Partial calling/rendering my html twice

I am new to knockout and sammy. I am implementing SPA using Sammy(router) and KnockOut(binding).
I have below code.
this.get('#/Page/:operation', function (context) {
this.partial('templates/Page1.html', { cache: false }).then(function (content) {
// the first argument to then is the content of the
// prev operation
$('#sammyDiv').html(content);
});
});
When I checked the console it's saying "You cannot apply bindings multiple times to the same element.(…)".
Am I doing anything wrong here?
What's the difference between Partial and Render?
Im guessing you are injecting new html into the parent which has already been bound. You should either be more specific about which divs you are binding too. I.E on apply bindings to the parent div of the html you injected, or clean and reapply bindings. I use both methods throughout my application.
if (isBound("my-div"))
ko.cleanNode(document.getElementById('my-div'));
ko.applyBindings(myModel, document.getElementById('my-div'));
Helper Function:
// Checks if Element has Already been Bound (Stops Errors Occuring if already bound as element can't be bound multiple times)
var isBound = function (id) {
if (document.getElementById(id) != null)
return !!ko.dataFor(document.getElementById(id));
else
return false;
};
I use this check before all my bindings as a safety net.
Thanks for the response and comments. Issue was with Sammy router.
this.get('#/Page/:operation', function (context) {
this.partial('templates/Page1.html', { cache: false }).then(function (content) {
// the first argument to then is the content of the
// prev operation
$('#sammyDiv').html(content);
});
});
changed to
this.get('#/Page/:operation', function (context) {
this.partial('templates/Page1.html', { cache: false });
});
It worked perfectly fine. Thanks again.

Programmatically set an observe block (instead of static object)?

Instead of:
Polymer({
observe: {
'key1': 'onDataChange',
'key2': 'onDataChange'
},
Can one programmatically execute an observer function for any data attribute data change (top-level)? I tried setting this.observe after creating an observe block object by looping over this.attributes, but that didn't work.
I don't think that there's support for "wildcards" in the keys of the observe block, but you can specify a space-separated list of attributes that share the same data-changed observer function.
E.g.
observe: {
'key1 key2': 'onDataChange'
}
You can see a real world example of it in the <google-youtube-video-wall> element.
(I'm not exactly sure why it's not documented, though. I'll follow up on that.)
An alternative to using an observe block is to add a callback for the attributeChanged element lifecycle event, e.g.:
Polymer({
// ...
attributeChanged: function(attrName, oldVal, newVal) {
console.log(attrName, 'old: ' + oldVal, 'new:', newVal);
}
// ...
});