Data-binding between nested polymer elements - data-binding

Suppose I have two distinct polymer-elements
One should be embedded inside the other using the content placeholder.
Is it possible to do data-binding between these two nested polymer-elements ?
I tried, but I can't get it to work: http://jsbin.com/IVodePuS/11/
According to http://www.polymer-project.org/articles/communication.html#binding data-binding between polymer-elements should work (in those examples they were done inside the template tag without using a content placeholder).
Update:
Scott Miles clarified that data-binding only works on the template level.
However in my case I don't know the exact template beforehand but I want to allow the user of my parent-element to specify which child-element it should contain (provided that there are different child-elements.
I think this question is related to this one: Using template defined in light dom inside a Polymer element
I updated the example below to highlight his:
<polymer-element name="parent-element" >
<template >
<div>Parent data: {{data1}} </div>
<content />
</template>
<script>
Polymer('parent-element', {
data1 : '',
ready: function() {
this.data='parent content';
}
});
</script>
</polymer-element>
<polymer-element name="child-element" attributes="data2">
<template>
<div>Parent data: {{data2}} </div>
</template>
<script>
Polymer('child-element', {
data2 : '',
ready: function() {
}
});
</script>
</polymer-element>
<polymer-element name="child2-element" attributes="data2">
<template>
<div>Parent data: {{data2}} </div>
</template>
<script>
Polymer('child2-element', {
data2 : '',
ready: function() {
}
});
</script>
</polymer-element>
The user can choose which child-element to embed:
<parent-element data1 = "test">
<child-element data2="{{data1}}"/>
</parent-element>
<parent-element data1 ="test" >
<child2-element data2="{{data1}}"/>
</parent-element>
Workaround:
The only workaround I found was to add change watcher and use getDistributedNodes() to get the child element and manually set data2 to data:
<polymer-element name="parent-element" >
<template >
<div>Parent data: {{data}} </div>
<content id="content"/>
</template>
<script>
Polymer('parent-element', {
data : '',
ready: function() {
this.data='parent content';
},
dataChanged : function() {
contents = this.$.content.getDistributedNodes();
if (contents.length > 0) {
contents[0].data2 = this.data;
}
},
});
</script>
</polymer-element>

Polymer data-binding works by attaching a model to a whole subtree.
You wrote:
<parent-element>
<child-element data2="{{data}}"/>
</parent-element>
this implies a rule that the parentNode provides the binding model. But now imagine you wanted to write:
<parent-element>
<div>
<child-element data2="{{data}}"></child-element>
</div>
</parent-element>
Now you have a problem.
Instead, in Polymer examples, you will notice that the {{}} are (almost always) inside of a template. For example, if I define:
<polymer-element name="host-element" attributes="data" noscript>
<template>
<parent-element data1="{{data}}">
<child-element data2="{{data}}"></child-element>
</parent-element>
</template>
</polymer-element>
Now, I have a model context (host-element) that I can use to bind things together in the entire subtree described by the template.
Note that I don't need attributes="data" for this to work. I added that so host-element exposes data and I can do this:
<host-element data="test"></host-element>
http://jsbin.com/IVodePuS/15/edit

Works like a charm. Parent element is publishing property data, that can by 2 way data bound. This way the child element gets the same data.
<polymer-element name="custom-element">
<template>
<parent data="{{data}}">
<child data="{{data}}"></child>
</parent>
</template>
<script>
Polymer({
ready: function() {
},
data: {}
});
</script>

I think in Polymer >= 1.0 this should be done using "Auto-binding template".

Related

In Polymer 1 how can I wrap a distributed template with another element?

I have a custom element (let's say my-view) which receives as effective children a template with some annotations for the data binding.
How can I wrap the distributed template with another custom element, let's say paper-item?
This is my working code.
<my-view>
<template>[[ item.name ]]</template>
</my-view>
Inside my-view I have
<template id="Repeater" is="dom-repeat">
</template>
and
_templatize() {
const repeater = this.$.Repeater
const template = this.queryEffectiveChildren('template')
repeater.templatize(template)
}
What I want to achieve is wrapping the template effective children with another custom element (let's say paper-item).
Something like
_templatize() {
const repeater = this.$.Repeater
const template = this.queryEffectiveChildren('template')
const item = this.create('paper-item')
item.appendChild(template.content)
repeater.templatize(item)
}
which of course doesn't work.
Perhaps I understood you wrong, but you don't create a page structure like the example you gave. Use the HTML elements first, and spice it up with javascript, if needed.
<dom-module id="my-view">
<template>
<template is="dom-repeat" items="[[anArrayWithStrings]]" as="someValue">
<paper-item>[[someValue]]</paper-item>
</template>
<template is="dom-repeat" items="[[anArrayWithObjects]]" as="employee">
<paper-item two-line>
<div>[[employee.name]]</div>
<div>[[employee.title]]</div>
</paper-item>
</template>
</template>
<script>
Polymer({
is: 'my-view',
properties: {
anArrayWithStrings: {
type: Array,
value: function() { return ['firstOne', 'secondOne', 'thirdOne']; }
},
anArrayWithObjects: {
type: Array,
value: function() { return [
{'name': 'Sarah', 'title': 'accountant'},
{'name': 'Ingrid', 'title': 'engineer'} ]; }
},
},
ready: function() {
//enter code here
},
</script>
</dom-module>
I just wrote this on freehand, without testing, so there could be some faulty code in there, but this is an example of how it could look like.

Accessing Firebase from Polymer Element

I am working on an app that uses Polymer and Firebase. I am trying to figure out how to access my Firebase document from my custom element. Currently, I have a view defined like this:
<body>
<template is="dom-bind" id="dialog">
<firebase-document location="https://my-app.firebaseio.com/" data="{{ orders }}"></firebase-document>
<iron-pages attr-for-selected="data-route" selected="{{route}}">
<section data-route="home">
<h3>Hello</h3>
</section>
<section data-route="orders">
<h3>Orders</h3>
<user-orders orders="{{ orders }}"></user-orders>
</section>
</iron-pages>
</template>
</body>
The user-orders element is defined like this:
<dom-module id="user-orders">
<template>
<template is="dom-repeat" items="[[ orders ]]" as="order">
<div>[[ order.date ]]</div>
<div>[[ order.status ]]</div>
<div>[[ order.description ]]</div>
</template>
<button on-click="test">Test</button>
<button>Add New Order</button>
</template>
<script>
Polymer({
is: "user-orders",
properties: {
orders: {
type: Array,
notify: true,
value: function() {
return [];
}
}
},
test: function() {
alert(JSON.stringify(this.orders));
}
})
</script>
</dom-module>
The view associated with my route appears correctly. However, when I click my "Test" button, an alert window appears that says null. Its like either a) I've setup my firebase connection improperly, which I'm not sure how to actually confirm or b) My data binding is setup improperly.
What am I doing wrong?
Are you working with a brand new Firebase app or an existing Firebase app? A brand new Firebase app without any data will return null.

Conditionally add css class in polymer

I have an element inside a polymer component and want to add a css class conditionally.
<div class="bottom {{completed: item.completed}}">
if item.isCompleted is true, then add the .completed class.
However, I've got the following error:
Invalid expression syntax: completed?: item.completed
I don't want to put the hole subtree inside a template conditional. Is it possible to do using an expression inside an html tag? I'm using the last polymer release, is this funtionallity migrated or replaced in any way?
The tokenlist technique was valid in Polymer 0.5, but has been deprecated.
As of Polymer 1.2, the following works:
<dom-module ...>
<template>
<div class$="bottom [[conditionalClass(item.completed)]]"></div>
</template>
<script>
Polymer({
...
conditionalClass: function(completed) {
return completed ? 'completed' : '';
}
});
<script>
</dom-module>
Also see similar question: Polymer 1.0 - Binding css classes
update Polymer 1.0
<div class$="[[getClasses(item.completed)]]">
getClasses: function (completed) {
var classes = 'bottom'
if(completed) classes += ' completed';
return classes;
}
Even when completed could be read from this.item.completed inside getClasses() it's important to pass it as parameter from the binding. This way Polymer re-evaluates the function getClasses(item.completed) each time item.completed changed.
original - Polymer 0.5
It should look like
<div class="bottom {{ {completed: item.completed } | tokenList }}">
See docs for more details: http://polymer-project.org/docs/polymer/expressions.html#tokenlist
the simplest way to do it:
<template>
<style is="custom-style">
.item-completed-true { background: red; }
</style>
<div class$="bottom item-completed-[[item.completed]]"></div>
</template>

passing values to meteor partials

I'm learning meteor to build quick website prototypes.
I'm trying to understand how to generate a set of values to populate the site templates and partials.
I have a layout.html template
<template name="layout">
<div class="container">
<header role="banner">
{{>site-header}}
</header>
<h1>This is {{siteLogo}}</h1>
<main role="main">
{{ yield }}
</main>
<footer role="contentinfo">
{{> site-footer }}
</footer>
</div>
</template>
in main.js I define the following:
Meteor.startup(function(){
Session.set('siteLogo', 'the logo');
});
Template.site-header.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
Template.layout.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
With this i can pass the value of siteLogo to layout.html.
I have a site-header.html partial
<template name="site-header">
<h1>{{siteLogo}}</h1>
</template>
I can't seem to be able to pass the value of siteLogo to the partial. Is there a way to do that?
Is it necessary to create a Session variable to pre-fill some values or can i just create a json settings list and access the value globally?
something that would go in main.js, like the yaml config file in a jekyll site:
siteSettings = [
{
siteLogo: "some brand name",
otherValue: "something else"
}
]
update
I'm a bit confused, I'm must be doing something wrong.
I've created a quick new meteor app to test this.
I have main.html
<head>
<title>handlebar-helper</title>
</head>
<body>
{{> header}}
{{> hello}}
{{> footer}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
<template name="header">
<header>
<h1>{{ headline }}</h1>
<p>tagline</p>
</header>
</template>
<template name="footer">
<footer role="contentinfo">
<h1>{{ headline }}</h1>
<small>copyright</small>
</footer>
</template>
And main.js
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to handlebar-helper.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Meteor.startup(function(){
Session.set('headline', 'My fancy headline');
});
Handlebars.registerHelper('headline', function(){
return Session.get('headline');
});
}
if (Meteor.isServer) {
// server code here
}
And i can't still pass the value of headline into >header of >footer
if I try to put the Session.set into the Meteor.isServer block, I get a syntax error, Session is not defined
Cheers
Do you have a Template.site-header.helpers function declared for siteLogo? If not it won't work - you can't use a helper from another template. If you need to use siteLogo in a variety of places, it's best to use a Handlebars block helper, as these can be accessed by any template.
UPDATE
The Handlebars helper would just look like this:
Handlebars.registerHelper('siteLogo', function() {
return Session.get('siteLogo');
});
However, if you've already got a siteLogo helper in the site-header Template, it suggests something else is wrong, like a typo in a template or helper name. Is there an error in the console?
UPDATE 2
If you want to use a dictionary-style structure to store reactive data, you can do something like this:
Session.set('myDict', {foo: 1, bar: 2});
Handlebars.registerHelper('myDict', function(key) {
return Session.get('myDict') ? Session.get('myDict')[key] : null;
});
And then use this in your template: {{myDict 'foo'}}. Obviously, the format above would work fine in a tempate helper as well, but it would only be accessible from within that template. The ternary operator is just to check that myDict has been initialised before it lets a template try to look up one of the keys, which is a common Meteor problem on page load.
Incidentally, if you're finding Session variables a cumbersome way to deal with reactive dictionary-like data structures, it's pretty easy to roll your own. This is the best introduction.

Reaching an object in the general context using "each" function of handlebars

I'm trying to use handlebars. It worked pefectly until I used the each function. The function works fine but it seems like it references everything to the parameters it takes. Therefore I cannot reach the object I want. I think a bit of code will be clearer anyway.
<div id="myDiv"></div>
<script type="text/x-handlebars-template" id="handlebar">
{{#each items}}
<p>{{this.name}} {{this.surname}}</p>
<p>{{somethingInTheGeneralContext}}</p>
{{/each}}
</script>
<script>
$( document ).ready(function() {
var source = $("#handlebar").html();
var template = Handlebars.compile(source);
var context = {
items: [
{
name:"Paul",
surname:"Buchon"
},
{
name:"Pierre",
surname:"Bino"
},
{
name:"Jean",
surname:"Duflou"
}
],
somethingInTheGeneralContext: "It works !"
};
var html = template(context);
$("#myDiv").html(html);
});
</script>
This prints :
Paul Buchon
Pierre Bino
Jean Duflou
And my somethingInTheGeneralContext is lost somewhere...
Any idea how I can fix it ?
Thanks!
I believe you can use {{../param}} to break out of the each context and go back to the parent level, where somethingInTheGeneralContext exists:
<script type="text/x-handlebars-template" id="handlebar">
{{#each items}}
<p>{{this.name}} {{this.surname}}</p>
<p>{{../somethingInTheGeneralContext}}</p>
{{/each}}
</script>

Resources