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);
}
Related
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
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>
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>
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).
I am updating an app to use RequireJS and move functions/variables out of the global context. I am not sure how to structure things when using JQuery click handlers so that my functions have access to the data-* attributes on the HTML object that triggered the click AND have access to properties inside of my module.
Below is a simple test case I put together to demonstrate what I am trying to do.
I want a function called on the click event for the div that is able to access both the data-value attribute on the div that triggered it and the packageVar property inside the module I'm loading with RequireJS.
Here is my index.html:
<!DOCTYPE html>
<head>
<title>Test.Next</title>
</head>
<body>
<script data-main="js/test-require" src="js/require.js"></script>
<div id="test-no-bind" data-value="value">Test No Bind</div>
<div id="test-bind" data-value="value">Test Bind</div>
</body>
</html>
here is the test-require.js:
requirejs.config({
"baseUrl": 'js',
"paths": {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min",
"domReady": 'vendor/domReady'
}
});
requirejs(['maintest','domReady!'], function(main) {
main.initialize();
});
And finally my maintest.js:
define(['jquery','domReady!'], function($) {
return {
'packageVar' : 'test',
buttonClick : function() {
console.log($(this).attr("data-value"));
console.log(this.packageVar);
},
initialize: function() {
$("#test-no-bind").click(this.buttonClick);
$("#test-bind").click(this.buttonClick.bind(this));
}
};
});
The test-no-bind div results in:
value
undefined
The test-bind div results in:
undefined
test
You're trying to access a properties from two different objects using the same context this. So in the no-bind case you're getting correctly the div element value for data-value attribute because inside jquery click function the this context it's the element where the event is produced, however you're getting and undefined for the other object property because the element doesn't have the packageVar property. For bind case where you are using bind function you're passing your object as this context so you've a value for packageVar but not for element attribute.
Try using the correct context to call buttonClick and passing the element context to the function in order to get each value correctly from his source:
define(['jquery','domReady!'], function($) {
return {
'packageVar' : 'test',
buttonClick : function(element) {
console.log($(element).attr("data-value"));
console.log(this.packageVar);
},
initialize: function() {
var that = this;
$("#test-no-bind").click(function() {
that.buttonClick(this);
});
$("#test-bind").click(function(){
that.buttonClick(this);
});
}
};
});
Try with this JSFIDDLE
Hope this helps,