I'm relatively new to Polymer and am trying to hide a table row in my custom element using the index of my named scope. It's not working at all, and I suspect I'm not on the right track. Could someone explain what I should be doing? Also, can {{index}} be used in element class names and IDs?
<link rel="import" href="bower_components/polymer/polymer.html">
<polymer-element name="task-lists" attributes="name">
<template repeat="{{t in tasks}}">
<template repeat="{{t, tindex in tasks}}">
<table>
<tr id="{{tindex}}">
<td>
<input type="checkbox" id="task1" class="checkBox" value="None" name="check" />
<label for="task1"></label>
<span on-click="{{hideLink}}">{{t.name}}</span>
</td>
</tr>
</table>
</template>
</template>
<script>
Polymer('task-lists', {
ready: function () {
this.tasks = [
{name: 'Painting'},
{name: 'Cleaning'}
]
},
hideLink: function () {
var row = document.getElementById("{{tindex}}");
row.display = 'none';
}
})
</script>
</polymer-element>
First issue:
You can't access the model properties directly in the event handler. Take a look at Event handling and data binding in the docs. You have to access the model associated with the template instance that created the event. Your event handler would then look something like this:
hidelink: function (e) {
var tindex = e.target.templateInstance.model.tindex;
...
}
Second issue:
document.getElementById only searches elements in the main document (light DOM) but the element you are looking for is in your element's shadow DOM, so you have to use this.shadowRoot.getElementById instead.
Related
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'm working with Web Components and try to bind a click event to an element inside of the Shadow DOM.
1. component.html included as <link rel="import" ...> inside of index.html
<template id="my-element">
<section>
<header>
<content select="h1"></content>
<button></button>
</header>
<content select="div"></content>
</section>
</template>
2. later element usage:
<my-element>
<h1>Headline</h1>
<div>...</div>
</my-element>
3. Access element and bind a function to it
Now I want to add an addEventListener() to the <button> inside of my <my-element> (which is unfortunately hidden through the #shadow-root). Like:
var elemBtn = document.querySelector('my-element button');
elemBtn.addEventListener('click', function(event) {
// do stuff
});
But that won't work. How do I achieve that?
You should be able to do this without involving the window object. Here's a full example:
<!-- Define element template -->
<template>
<h1>Hello World</h1>
<button id="btn">Click me</button>
</template>
<!-- Create custom element definition -->
<script>
var tmpl = document.querySelector('template');
var WidgetProto = Object.create(HTMLElement.prototype);
WidgetProto.createdCallback = function() {
var root = this.createShadowRoot();
root.appendChild(document.importNode(tmpl.content, true));
// Grab a reference to the button in the shadow root
var btn = root.querySelector('#btn');
// Handle the button's click event
btn.addEventListener('click', this.fireBtn.bind(this));
};
// Dispatch a custom event when the button is clicked
WidgetProto.fireBtn = function() {
this.dispatchEvent(new Event('btn-clicked'));
};
var Widget = document.registerElement('my-widget', {
prototype: WidgetProto
});
</script>
<!-- Use the element -->
<my-widget></my-widget>
<!-- Listen for its click event -->
<script>
var widget = document.querySelector('my-widget');
widget.addEventListener('btn-clicked', function() {
alert('the button was clicked');
});
</script>
Example on jsbin
I found out that creating a custom createEvent('MouseEvent'); inside of <template> will do the trick!
TL;DR http://jsfiddle.net/morkro/z0vbh11v/
1. First, you need to add the onclick=""-attribute to our <template> and create a custom event:
<template id="my-element">
<section>
<header>
<content select="h1"></content>
<button onclick="callEventOnBtn()"></button>
</header>
<content select="div"></content>
</section>
<script>
var btnEvent = document.createEvent('MouseEvent');
btnEvent.initEvent('oncomponentbtn', true, true);
var callEventOnBtn = function() {
window.dispatchEvent(btnEvent);
};
</script>
</template>
I create the custom event inside of the <template> and automatically dispatch it to the global window object when the Custom Element gets used later.
2. Now we can listen to that event, when clicking the <button> on our custom element
window.addEventListener('oncomponentbtn', function(event) {
// do stuff
});
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);
I'm wondering how could I instance a Polymer element when I click on another element. Is there a way to instance window-base from dock-icon? (code down). I though I could use the constructor every element has but I can't figure out how this works. How could I pass a variable to that constructor.
Code of the two elements involved:
<polymer-element name="dock-icon" attributes="name" on-click="{{click}}">
<template>
<link rel="stylesheet" href="dock-icon.css">
</template>
<script>
Polymer('dock-icon', {
name: "",
click: function (event, detail, sender) {
alert(this.name);
//instance <window-base> and pass name parameter
}
});
</script>
Polymer element that has to be instanced
<polymer-element name="window-base" attributes="name height width left top">
<template>
<link rel="stylesheet" href="window-base.css">
<div id="box">
<header id="header"><h2>{{name}}</h2></header>
</div>
</template>
<script>
Polymer('window-base', {
name: "name",
//more stuff here
});
</script>
Thanks
This should be straightforward as:
var el = document.createElement('window-base');
el.name = 'some name';
This is my view for a single row as "tr". I want want to click on the name cell and pop up a view for that cell. I could not get the event firing..
am I missing something? Thanks!
So this issue is solved by gumballhead, the issue I was having is that there needs to be a tagName associated with the ItemRowView. and then in the render function, I need to do self.$el.html(this.template(model));
Thought it might be helpful to share with..
ItemRowView = Backbone.View.extend({
initialize : function() {
},
template : _.template($('#item-row-template').html()),
render : function() {
var self=this;
var model = this.model.toJSON();
self.$el = this.template(model);
return self.$el;
},
events : {
"click td .item-name" : "viewOneItem"
//Even if I change it to "click td":"viewOneItem", still not firing
},
viewOneItem : function() {
console.log("click");
}
});
collection View:
ItemsView = Backbone.View.extend({
initialize : function() {
},
tagName : "tbody",
render : function() {
var self = this;
this.collection.each(function(i) {
var itemRowView = new ItemRowView({
model : i
});
self.$el.append(itemRowView.render());
});
return self.$el;
}
});
app view:
AppView = Backbone.View.extend({
this.items = new Items();
this.items.fetch();
this.itemsView = new ItemsView({collection:this.items});
$('#items-tbody').html(itemsView.render());
});
for template:
<script type="text/template" id="item-row-template">
<tr>
<td class="item-name">{{name}}</td>
<td>{{description}}</td>
</tr>
</script>
<table>
<thead>
<tr>
<th>name</th>
</tr>
</thead>
<tbody id="items-tbody">
</tbody>
</table>
Use "click td.item-name" for your selector. You are currently listening for clicks on a descendant of td with the class "item-name".
FYI, you've also got a closing tag for an anchor element without an opening tag in your template.
Edit: I think you want self.$el.html(this.template(model)); rather than self.$el = this.template(model);
But there's no need to alias this to self with the code you posted.
Edit 2: Glad you got it sorted out. Let me give you an explanation.
All Backbone Views need a root element. That's the element that the events in the events hash are delegated to on instantiation. When a Backbone View is instantiated without an existing element, it will create one based on configuration settings like tagName, whose default is "div". The element won't appear in the DOM until you explicitly inject it.
So when you set self.$el in your render method, you were overwriting the root element (along with the events, though they would have never fired because it would have listened for a click on a td that was a descendant of a div that didn't exist in the DOM).
As a side note, and it would not be the right way to do it in your case, you could have done this.setElement($(this.template(model)); to redelegate the events from the div created on instantation to the tr created by your original template.