I'm trying to bind a method to an on-tap attribute of a paper-button. After much testing, I've found that I can only bind a (for lack of a better word) top-level function, and not a method of an object in the template.
For example, I have a template, to which I have bound a number of objects, one of which is a user object. Object user has a bunch of methods and variables, like 'isNew' or 'reputation'. The user object also has a method 'addReputation'
I can use the object variables like this :
<template if = '{{user.new}}'><h1>{{user.name}}</h1></template>
And I can bind button taps like this:
<paper-button on-tap='{{addReputation}}'>Add Rep</paper-button>
But not like this:
<paper-button on-tap='{{user.addReputation}}'>Add Rep</paper-button>
Does anyone know why this may be?
if you set the method to a handler on your element's prototype it works. That way you can still keep things dynamic:
<script src="http://www.polymer-project.org/webcomponents.min.js"></script>
<script src="http://www.polymer-project.org/polymer.js"></script>
<polymer-element name="my-element" on-tap="{{tapHandler}}">
<template>
<style>
:host {
display: block;
}
</style>
click me
<content></content>
</template>
<script>
Polymer({
created: function() {
this.user = {
method: function() {
alert('hi');
}
};
this.tapHandler = this.user.method;
}
});
</script>
</polymer-element>
<my-element></my-element>
i'm sharing my plunk to resolve above problem. plunk link
In the template
<button on-tap="{{fncall}}" data-fnname="b">b call</button>
In the script
x.fncall = function(e) {
var target = e.target;
var fnName = target.getAttribute("data-fnname");
return x.datamodel[fnName]();
}
Polymer(x);
Related
So basically what I'm struggling with is how to pass the reference of the parent element to its child element i.e the custom remove element?
can anyone please help me out!
*******************this is the el-insert element********************
// this element recieves data from another element(username and comment)
<link rel="import" href="./remove.html">
<dom-module id="el-insert">
<template >
<div id="userComment"><span>{{username}}</span>: {{saveComment}}</div>
<input id="edit" type="text" value={{saveComment::input}}>
<hr>
<reply-comment></reply-comment>
<button on-click="edit">Edit</button>
<remove-comment ></remove-comment>
</template>
<script>
class elInsert extends Polymer.Element {
static get is() { return 'el-insert'; }
static get properties(){
return {
saveComment:{
type:String,
notify:true
},
username:{
type:String,
notify:true
}
}//return ends
}
edit (){
$(this.$.userComment).toggleClass('hide');
var display = $(this.$.edit).css('display');
if(display == 'none'){
$(this.$.edit).css('display','block');
}else{
$(this.$.edit).css('display','none');
}
}
}
window.customElements.define(elInsert.is, elInsert);
</script>
</dom-module>
***************the remove button element******************
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="remove-comment">
<template>
<button on-click="remove">el-Remove</button>
</template>
<script>
class removeComment extends Polymer.Element {
static get is() { return 'remove-comment'; }
static get properties(){
return{
}//return ends
}
remove(){
var element = this.parentNode.host;
$(element).remove();
}
}
window.customElements.define(removeComment.is,removeComment);
</script>
</dom-module>
This code works perfectly for me.
But how will I do the same thing using fire or dispatch as mentioned in the answer by Nicolas.
How to pass event details from parent element to child element?
Also, I want to make this element reusable so that I can simply drop it into another place like in a reply to delete that also.
(Also, I'm new very new to polymer so if there is anything else that I can improve upon in this code then please let me know)
I hope now you guys can help me out.
You do not want to do that - in general you should just pass properties to the child element and catch events from the child in the parent elements.
properties go down and events go up -
In you case you do not have to pass anything to the child element. The child element should just fire an event and the parent should respond to it when it catches it.
Something like that:
<dom-module id="child-element">
<template>
<div on-tap="_deleteComment"></div>
</template>
<script>
class ChildElement extends Polymer.Element {
...
_deleteComment() : {
// fire event to be caught by parent
const deleteEvent = new CustomEvent('deleteComment', {detail: {
whatever: {
you: 'want',
},
}});
this.dispatchEvent(deleteEvent);
}
}
...
</script>
</dom-module>
<dom-module id="parent-element">
<template>
<child-element on-delete-comment="_deleteComment"></child-element>
</template>
<script>
class ParentElement extends Polymer.Element {
...
_deleteComment(evt) : {
// handle evt
}
}
...
</script>
</dom-module>
#daKmoR is right - more context and some code would help -
I am working on a project that makes use of Firebase Authentication and <firebase-query> from the polymerfire elements. I use data binding in many places throughout my application and never had this problem.
I bind the user object, which was created when a user authenticated, in many places to receive the name of my users. The following code shows a custom element. In there, I am trying to bind besides the user object the Firebase snapshot to a property that is of type Object.
When I console.log() the vidobj property, it displays the whole object. However, I am unable to bind it to my text. Although, the same works for the user object property.
I believe this has something to do with the lifecycle in Polymer. Should the property not update automatically even though the value might be created later?
The following screenshot displays the two console.log's
with the content of the vidobj:
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="my-singlevideo">
<template>
<style>
:host {
display: block;
}
</style>
<firebase-auth user="{{user}}"></firebase-auth>
<iron-localstorage
id="localstorage"
name="my-app-storage"
value="{{localUserDetails}}">
</iron-localstorage>
<h1>Name: [[user.displayName]]</h1>
<h1>Video Title: [[vidobj.title]]</h1>
</template>
<script>
Polymer({
is: 'my-singlevideo',
properties: {
user: {
type: Object,
},
localUserDetails: {
type: Object,
},
vidobj: {
type: Object,
},
},
ready: function() {
this.$.localstorage.reload();
var videoId = this.localUserDetails.lastClickedVid;
firebase.database().ref('/videos/' + videoId).once('value', function(snapshot) {
this.vidobj = snapshot.val();
console.log(this.vidobj);
console.log(this.vidobj.title);
});
},
});
</script>
</dom-module>
Your Firebase callback's context is not bound to your Polymer object, so you're actually setting vidobj on the outer context (usually the Window object).
To fix this, use Function#bind like this:
ready: function() {
// ...
firebase.database().ref('/videos/' + videoId).once('value',
function(snapshot) {
this.vidobj = snapshot.val();
console.log(this.vidobj);
console.log(this.vidobj.title);
}.bind(this)
);
}
I'm trying to make custom element within a custom element and have the inner custom element able to change its value from within and the outer able sync its binding on that change. What can I do to get this working?
I've thoroughly scoured the documentation, but it's very lackluster in this department. I believe Node.bind() may be something of interest, but not sure how it would apply in this case.
Here's a simplified test case and plunker demo:
<polymer-element name='test-app'>
<template>
First:
<test-input id='one' value='{{value}}'></test-input>
<br/>
<br/>
Second:
<test-input id='two' value='{{value}}'></test-input>
<br/>
<br/>
Value:
{{value}}
</template>
<script>
Polymer({
value: 5
})
</script>
</polymer-element>
<polymer-element name='test-input'>
<template>
<style>
#val {
font-size: 50px;
}
</style>
<div id='val'>{{value}}</div>
<button on-tap='{{increment}}'>+</button>
<button on-tap='{{decrement}}'>-</button>
</template>
<script>
Polymer({
publish: {
value: 4
},
increment: function() {
this.value = this.value + 1;
},
decrement: function() {
this.value = this.value - 1;
}
})
</script>
</polymer-element>
<test-app></test-app>
http://plnkr.co/edit/KjQ9DusaFg2jp1BTFUde?p=preview
If this was working, the value property of the test-app parent element should be in sync with both of the test-inputs value property.
Notice this warning in the console:
Attributes on test-input were data bound prior to Polymer upgrading the element. This may result in incorrect binding types.
test-app uses test-input before Polymer knows about test-input. All of an element's dependencies must be declared before that element is declared. Move test-input above test-app and it works as expected.
http://plnkr.co/edit/ZaIj60S3lAHT18k5T3sn?p=preview
I have an object variable in an Angular controller with some properties. I want to create a series of polymer elements that takes that variable an the name of the property and shows them in a specific format (depending of type and other attributes). Something like the next example:
<polymer-element name="x-property" attributes="data property">
<template>
{{data.labels[property]}}: {{data[property]}}
</template>
<script>
Polymer('x-property', {
data: {}
});
</script>
</polymer-element>
And then I use it as this:
<x-property data="{{person}}" property="firstName"></x-property>
That works just fine. But now I want to avoid to specify the attribute data in all the elements. Reading Polymer documentation I see that it is possible to have global variables. I followed the example created the app-globals element, as shown in the api guide but when I try to access the property, instead of having the object "person" I got the text "{{person}}"
<polymer-element name="app-globals" attributes="values">
<script>
(function () {
var values = {};
Polymer('app-globals', {
ready: function () {
this.values = values;
for (var i = 0; i < this.attributes.length; ++i) {
var attr = this.attributes[i];
values[attr.nodeName] = attr.value;
}
}
});
})();
</script>
</polymer-element>
<polymer-element name="x-property" attributes="property">
<template>
<app-globals id="globals" values="{{globals}}"></app-globals>
{{globals.data.labels[property]}}: {{globals.data[property]}}
{{globals.data}}
</template>
<script>
Polymer('x-property', {
});
</script>
</polymer-element>
So in my html I have:
<app-globals data="{{person}}"></app-globals>
<x-property property="firstName"></x-property>
And the result I get is just this:
:
{{person}}
Is there anyway I can make this work as it works in the first example?
Because attributes are only strings, this code values[attr.nodeName] = attr.value; cannot capture your object-valued data. Instead, JavaScript converts your object to a string, which is why you see [Object object].
Capturing objects with mustaches ({{ }}) is a special Polymer feature that you enable by publishing the property as an attribute (or listing it in the publish map in the prototype).
If, instead of making app-globals generic, we instead publish values and data directly, then we can make it work like so:
<polymer-element name="app-globals" attributes="values data">
<script>
(function () {
var values = {};
Polymer('app-globals', {
created: function() {
this.values = values;
},
dataChanged: function() {
this.values.data = this.data;
}
});
})();
</script>
</polymer-element>
<polymer-element name="x-property" attributes="property">
<template>
<app-globals values="{{globals}}"></app-globals>
{{globals.data.labels[property]}}: {{globals.data[property]}}
{{globals.data | json}}
</template>
<script>
Polymer('x-property', {
json: function(s) {
return JSON.stringify(s);
}
});
</script>
</polymer-element>
Your original code doesn't have the json filter, but otherwise we are again asking Javascript to put an object in a string context, and it will render [Object object].
http://jsbin.com/vusayo/10/edit
How can I get JS access to .content of nested <template>?
I am trying to extend <template> with my imported-template element (which fetches template content from external file) and I would like to implement <imported-content> in similar manner to native <content>. To do so, I simply try
this.content.querySelector("imported-content")
but it occurred, that for nested template this.content is empty.
<script>
(function() {
var XHTMLPrototype = Object.create((HTMLTemplateElement || HTMLElement).prototype);
XHTMLPrototype.attachedCallback = function() {
//..
var distributeHere = this.content.querySelector("imported-content");
var importedContent = document.createElement("span");
importedContent.innerHTML = "Imported content";
distributeHere.parentNode.replaceChild(importedContent, distributeHere);
}
document.register('imported-template', {
prototype: XHTMLPrototype,
extends: "template"
});
})();
</script>
<template id="fails" bind>
<ul>
<template is="imported-template" bind="{{ appdata }}">
<li>
<imported-content></imported-content>
</li>
</template>
</ul>
</template>
JSFiddle here
I am not sure if it is a bug, a design issue, or just template shim limitation.
I thought that maybe I am checking it in wrong life-cycle callback, so I tried MutationObserver fiddle here, but mutation does not occur as well.
I changed your selector to this.content.querySelector("[is='imported-content']"). Is this what your trying to do?