Data binding in Polymer - function is being removed from bound object - javascript

I'm encountering an issue binding an object that contains a function from angular to Polymer 1.0. The function is not being passed through into the target object in the custom element. Here is a simplified code sample:
The custom element has a single property named myprop:
<script>
Polymer({
is: 'my-custom-element',
properties: {
myprop: Object
},
attached: function () {
var x = this.myprop.x; //this is ok
this.myprop.myfunc(); //myfunc is not defined!
}
});
</script>
Here is the HTML:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<my-custom-element myprop="{{myobject}}"></my-custom-element>
</div>
</div>
And here is the angular controller:
<script>
angular.module("myApp", []).controller("myCtrl", function ($scope) {
$scope.myobject= {
x: 4,
myfunc: function() {
//function body
}
}
});
</script>
Why isn't the function available in the custom element?

As documented here: https://github.com/Polymer/polymer/blob/3e96425bf0e0ba49b5f1f2fd2b6008e45a206692/PRIMER.md#attribute-deserialization
... objects passed into polymer elements are being passed through JSON.stringify and then JSON.parse (depending on variable type).
Functions will be completely stripped out by JSON.stringify - just checkout out this sample...
console.log( JSON.stringify({x:123,y:function(){ return 123; }}) );
// outputs: {"x":123}
I believe this is the offending line in source...
https://github.com/Polymer/polymer/blob/3b0d10b4da804703d493da7bd0b5c22fc6f7b173/src/micro/attributes.html#L232
... and comments nearby suggest possibility to change this behavior...
Users may override this method on Polymer element prototypes to provide serialization for custom types

You can't call Angular function like you write this.myprop.myfunc();
I can't explain why this is so, but if you want call Angular function from Polymer you can use this.fire('nameEvent') and in Angular controller or run module add event listener like
document.addEventListener('nameEvent', function() {
//function body
})
I hope that help you. Good luck

I'm not simulating with Angular but I think that {{myobject}} can have a problem. Only with Polymer works fine.
Basically I copied your code in the my-element and created my-element-two where I import the it. The result is "My name" printed in the lifecycle attached.
<link rel="import" href="../polymer/polymer.html">
<dom-module id="my-element">
<script>
Polymer({
is: 'my-element',
properties: {
myprop: Object,
},
attached: function () {
var x = this.myprop.x; //this is ok
this.myprop.myfunc(); //myfunc is not defined!
}
});
</script>
</dom-module>
<dom-module id="my-element-two">
<template>
<my-element myprop="{{myobject}}"></my-element>
</template>
<script>
Polymer({
is: 'my-element-two',
properties: {
myobject: {
type: Object,
value: {
x: 4,
myfunc: function() {
console.log("My name");
}
}
}
},
});
</script>
</dom-module>
<!-- results is print "My name" in the console. -->
<my-element-two></my-element-two>

Related

Accessing a method of element inside a callback of listener for a node inside the same element

I am having issues accessing a method inside a dom in the callback of a node that I've attached a click listener to and I can't figure out why. I have tried the followings.
Here's the piece of code responsible to accessing the method inside callback:
<dom-module id="my-view2">
<template>
...html codes ...
</template>
<script>
Polymer({
is: 'my-view2',
... properties, etc ...
//******* trying to access a method *******************
handleClick: function(e) {
var btn = document.createElement("paper-button");
btn.addEventListener("click", function() {
var el = Polymer.dom(this).querySelector('#my-view2');
el.methodToAccess();
});
}
methodToAccess: function() {
console.log('success');
},
});
I have tried all of these:
this.$$('#my-view2")
document.querySelector("#my-view2");
Polymer.dom(this.root).querySelector('#my-view2');
Polymer.dom(this).querySelector('#my-view2');
this.getElementById('my-view2');
none of them works and all result in Cannot read property 'methodToAccess' of null. What am I doing wrong?
If you bind the callback function to the current this scope, you will have direct access to methodToAccess.
handleClick: function(e) {
var btn = document.createElement("paper-button");
btn.addEventListener("click", function() {
this.methodToAccess();
}.bind(this));
}
That being said you may want to do it the polymer way using imperative add/remove listeners:
this.listen(this.$.myButton, 'tap', 'onTap');
this.unlisten(this.$.myButton, 'tap', 'onTap');
HTH

How to pass parameter to Polymer event listener?

How can I pass parameters to an annotated event listener?
I have this code:
<paper-button id="bt_close" raised on-tap="close">
Close first movement
</paper-button>
And I´m trying to call my close function with some argument (i.e., on-tap="close(1)" or close(2) or close(custom_parameter), and so on).
My close function is:
close: function (p) {
console.log(p);
}
But I´m seeing this error in the console:
listener method close(1) not defined
The event annotation requires the name of a method in your Polymer object definition. At runtime, Polymer looks up the method by name verbatim on the object, so including parameters in the method name would cause the lookup to fail, and you'd see the console error you've mentioned.
To specify an argument, you could use use a data attribute like this:
// template
<paper-button on-tap="close" data-foo="1">Close</paper-button>
// script
Polymer({
_close: function(e) {
const foo = e.target.dataset.foo;
// do something with foo
}
...
});
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
_close: function(e) {
const foo = e.target.dataset.foo;
console.log('foo', foo);
}
});
});
<head>
<base href="https://cdn.rawgit.com/download/polymer-cdn/1.8.0/lib/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="paper-button/paper-button.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<paper-button on-tap="_close" data-foo="1">Close</paper-button>
</template>
</dom-module>
</body>
codepen

declared property being initialized multiple times when using data binding

Polymer v1.2.3
I am running into an issue with Polymer and I'm not sure if its a bug or not.
I have this property that I need to set to null after doing something with it in the ready call back, however it seems like when I am initializing the property via data binding, the property gets re-initialized after I set it to null.
Take the following example:
proxy-elm.html
<dom-module id="proxy-elm">
<template>
<x-foo it="[[prox]]"></x-foo>
</template>
<script>
Polymer({
is: 'proxy-elm',
properties: {
prox: String
}
});
</script>
</dom-module>
x-foo.html
<dom-module id="x-foo">
<template>
<div>...</div>
</template>
<script>
Polymer({
is: 'x-foo',
properties: {
it: String
},
ready: function () {
//some processing stuff
console.log(this.it);
this.it = '';
setTimeout(function () {
console.log("later -- " + this.it);
}.bind(this), 3000);
}
});
</script>
</dom-module>
Main.html
<proxy-elm prox="hi"></proxy-elm>
Console
=>hi
=>
=>later -- hi
This issue only occurs then the proxy element uses data binding. If I have the proxy element set it to an immediate, the console looks like:
=>hi
=>
=> later --
Which is the desired behavior.
What's up with this? I'm going mad!
This is probably due to the initialization order (cf. Documentation)
You can try in the ready() function from your proxy to set the value of x-foo as desired like this:
<dom-module id="proxy-elm">
<template>
<x-foo id="xFoo" it="[[prox]]"></x-foo>
</template>
<script>
Polymer({
is: 'proxy-elm',
properties: {
prox: String
},
ready: function () {
this.$.xFoo.it = '';
}
});
</script>
</dom-module>
and keep the ready() function of x-foo for your specific processing!
This is indeed confusing. However, when reading the documentation very carefully, I noticed that it only mentions local dom.
It is called after the element’s template has been stamped and all elements inside the element’s local DOM have been configured (with values bound from parents, deserialized attributes, or else default values) and had their ready method called.
So the local children of your element are guaranteed to have been initialized, but not necessarily the attributes of your element.
I noticed that wrapping the code in an async helps.
ready: function () {
//some processing stuff
console.log(this.it);
this.async(function(){
this.it = '';
});
setTimeout(function () {
console.log("later -- " + this.it);
}.bind(this), 3000);
}

How to call direct parent element function

Please consider following example:
<script src="https://cdnjs.cloudflare.com/ajax/libs/polymer/0.5.5/polymer.js"></script>
<polymer-element name="my-outer" >
<template>
outer
<my-inner id="inner" >
<button on-click="{{buttonClick}}">push</button>
</my-inner>
outer
</template>
<script>
Polymer('my-outer', {
buttonClick: function(){
// it wasn't commented before update
//this.$.inner.buttonClick();
}
});
</script>
</polymer-element>
<polymer-element name="my-inner">
<template>
<content></content>
</template>
<script>
Polymer('my-inner', {
// update
domReady: function() {
this.eventController = this;
},
//end update
buttonClick: function(){
alert('inner');
}
});
</script>
</polymer-element>
<my-outer></my-outer>
The question is if there is any way to call Inner's buttonClick without explicit delegation in outer (putting $.inner... into on-click also doesn't work)? Maybe with bind or something.
Best reagrds, Eugene.
ps: the snippet works only in chrome. Not sure if it's polymer or stackoverflow to blame.
UPDATE: looks like putting
domReady: function() {
this.eventController = this;
},
into inner component solves the problem. But I still don't fully undestand what that eventController is. When event handler is looking for one it just iterates through node parents and picks up any with defined eventController, if none is found then node.host is used. The only other place where eventController pops up in polymer code is some lightFromTemplate function with murky comment and TODO on it.Then there is also bug https://github.com/Polymer/polymer/issues/1170 and couple of posts which do not make the issue clear for me.
So the updated question is:
What is polymer's eventController and are there any ways to work with it?
It is possible using data-binding, but this solution is not elegant. I'd much more prefer your initial solution.
<script src="https://cdnjs.cloudflare.com/ajax/libs/polymer/0.5.5/polymer.js"></script>
<polymer-element name="my-outer">
<template>
outer
<my-inner id="inner" clicked="{{clicked}}">
<button on-tap="{{click}}">push</button>
</my-inner>
outer
</template>
<script>
Polymer({
clicked: false,
click: function() {
this.clicked = !this.clicked;
}
});
</script>
</polymer-element>
<polymer-element name="my-inner">
<template>
<content></content>
</template>
<script>
Polymer('my-inner', {
publish: {
clicked: {
value: false,
reflect: true
}
},
clickedChanged: function() {
alert('inner');
}
});
</script>
</polymer-element>
<my-outer></my-outer>

Polymer Node to element attribute

I have next polymer element:
<link rel="import" href="../../lib/polymer/polymer.html">
<polymer-element name="ss-timeline" attributes="musicChooser photoSelector">
<template>
<div></div>
</template>
<script type="text/javascript">
(function() {
"use strict";
Polymer('ss-timeline', {
created: function(){
this.musicChooser = this.musicChooser || {};
this.photoSelector = this.photoSelector || {};
},
ready: function () {
console.log(this.musicChooser, this.photoSelector);
},
musicChooserChanged: function(oldVal, newVal) {
console.log('musicChooserChanged', oldVal, newVal);
}
});
})();
</script>
</polymer-element>
So I have to pass into the musicChooser and photoSelector Node objects like so:
<ss-music-chooser id="musicChooser"></ss-music-chooser>
<ss-timeline musicChooser="{{$.musicChooser}}"></ss-timeline>
But in console output I get:
Object {} Object {}
How can I pass Node objects by the attributes? Please help.
P.S. I saw that core-dropdown has same relatedTarget attribute:
<core-icon-button id="trigger" icon="menu"></core-icon-button>
<core-dropdown relatedTarget="{{$.trigger}}">
<core-menu>
<core-item>Cut</core-item>
<core-item>Copy</core-item>
<core-item>Paste</core-item>
</core-menu>
</core-dropdown>
So this should work in theory...
P.P.S. By the way if I pass in attribute musicChooser="123" I get it in my element. But node object ignored for some reason.
Your changed watcher should work, here's an example.
I'm wondering if the issue has to do with your use of automatic node finding. The $ is only going to work inside of another Polymer element (and possibly also inside of an auto-binding template, but I'd need to double check).

Categories