I have a Polymer app that has some nested components. I'm trying to share a property value across the components. My thought was that I could do this with data-binding. However, I have been unsuccessful in my attempt, which can be seen in this Plunkr. The relevant code (I think) is here:
<neon-animated-pages selected="[[selectedPageIndex]]" style="height:100%;">
<view-1 is-enabled="{{ isEnabled }}"></view-1>
<view-2 is-enabled="{{ isEnabled }}"></view-2>
</neon-animated-pages>
Each view has a paper-toggle-button. If the toggle button is set in one view, I want that same value to appear in the other view. Yet, that is not what's happening. It's like each view is using it's own isEnabled value. As the Plunkr shows, I've done the following:
Created a property called isEnabled in each view ("view-1.html" and "view-2.html").
Created a property called isEnabled in the hosting component ("shell.html")
Used two-way binding via the {{ }} curly-brace syntax.
What am I doing wrong? Why isn't the isEnabled value set in one view propogating to the other?
Thank you!
You should set notify to true in the definition of the isEnabled property in you views. This is needed for two-way data-binding (docs).
First: Name your element files the way you name your elements. Change shell to app-shell in your directory.
Second: What user Maria said, just declare a notify: true property to each element you want to be able to databind, like this:
<dom-module id="view-1">
<template>
<h2>View 1</h2>
<paper-toggle-button checked="{{ isEnabled }}">Is Enabled?</paper-toggle-button>
</template>
<script>
Polymer({
is: 'view-1',
properties: {
isEnabled: {
type: Boolean,
value: false,
notify: true
}
}
});
</script>
</dom-module>
Do the same for the view-2 property.
Here is your plunkr with working two-way data binding:
http://plnkr.co/edit/YhjE02O14YGCErXu9Vtq
Hope it helps you.
Related
The polymer documentation has the following two way binding example:
<script>
Polymer({
is: 'custom-element',
properties: {
someProp: {
type: String,
notify: true
}
}
});
</script>
...
<!-- changes to "value" propagate downward to "someProp" on child -->
<!-- changes to "someProp" propagate upward to "value" on host -->
<custom-element some-prop="{{value}}"></custom-element>
I'm looking for a complete example that includes the design of the child, programmatic and interactive events the can cause upward and downward propagation of the `{{value}} property, and a demo of of the complete setup / example.
Here are some examples on js fiddle that demonstrate different ways of binding:
Two-way binding:
https://jsfiddle.net/tej70osf/
One-way binding: notify is not set on value property of the child element:
https://jsfiddle.net/tej70osf/1/
One-way binding: notify is set to true true on value property of the child element however the value property is bound using square brackets [[value]] instead of {{value}}:
https://jsfiddle.net/tej70osf/2/
Hope that helps
<dom-module id="user-demo">
<template>
<paper-input label="FIRST NAME" value="{{firstName}}"></paper-input>
</template>
</dom-module>
<user-demo></user-demo>
In your javascript code:
Polymer({
is: 'user-demo',
properties: {
firstName: {
type: String,
value: 'John',
notify: true
}
}
});
Check out the following fiddle for the full example:
https://jsfiddle.net/meenakshi_dhanani/6ffwh0qv/
I tried to use more polymer elements and two way binding. Hope it helps
I'm just learning polymer (1.0) so please bear with me.
I'm using express.js to return some array of JSON.stringified items and for-each them, so the result is as follows (in HTML):
<fighter-profile fighter="{"country":"USA","countryFullName":"United States","name":"Frank Mir","nickname":"","zuffa_record":{"wins":"15","losses":"9","draws":0,"no_contest":0}}"></fighter-profile>
it seems ugly as hell, but that's json.
Here's my component:
<dom-module id="fighter-profile">
<template>
<div>
<paper-item>
<paper-item-body two-line>
<div>{{fighter.name}}</div>
<div secondary>{{nickname}}</div>
<div>
<paper-button raised on-click="handleClick">Show nickname</paper-button>
</div>
</paper-item-body>
</paper-item>
</div>
<br />
<hr />
<br />
</template>
<script>
Polymer({
is: 'fighter-profile',
properties: {
fighter: Object,
nickname: {
type: String,
value: 'testing'
}
},
ready: function() {
this.nickname = (this.fighter.nickname !== '') ? this.fighter.nickname : '... the dude has no nickname!';
},
handleClick: function() {
alert(this.nickname);
}
});
</script>
</dom-module>
Now, the funny part: the name gets displayed properly, while where I have the <div secondary>{{nickname}}</div>, the result in HTML is literally {{nickname}}; however, if I click on button, I get the correct value.
What am I missing here?
UPDATE:
I've googled some stuff, and replaced ready method with created and, of course, it didn't work, since created I think is part of Polymer 0.5 version. Then I switched back to ready method and now everything works as expected. Very odd.
What seems to be the problem? Some caching gone wrong? a bug?
UPDATE 2:
I've changed some stuff again and it doesn't work, but now I've figured out how to replicate the mistake. So, this piece of code DOESN'T work correctly:
<div secondary>The dude is also known as {{nickname}}</div>
the result is literally "{{nickname}}"
However, this works correctly:
<div secondary>The dude is also known as <span>{{nickname}}</span></div>
the result is the actual nickname.
So, putting properties in span tag renders it correctly. What's going on?
There's a few things I think I can help you with here. First, you can make your JSON much more readable by using single quotes for your attributes. Additionally, you can include white space, if you are hard-coding the JSON:
<fighter-profile
fighter='{
"country":"USA",
"countryFullName":"United States",
"name":"Frank Mir",
"nickname":"",
"zuffa_record":{
"wins":"15",
"losses":"9",
"draws":0,
"no_contest":0
}
}'></fighter-profile>
Next, I'm going to assume that the JSON is actually not hard-coded, and bound to another data source. I make this assumption because it seems like your fighter property is not available in ready, as you are expecting it to be. A common issue I see in cases such as this is something like the following:
<template is="dom-repeat" items="{{data}}" as="fighter">
<fighter-profile fighter="{{fighter}}"></fighter-profile>
</template>
The thing to keep in mind in the above case is that <fighter-profile> is created, readied, and attached to the DOM before the parent element assigns fighter to its fighter property.
To remedy this, you can make use of observers which perform tasks automatically when the data gets loaded into a property:
<script>
Polymer({
is: 'fighter-profile',
properties: {
fighter: Object,
nickname: {
type: String,
value: 'testing'
}
},
observers: [
// This tells Polymer to watch `fighter` and fire the
// _fighterUpdated method only after `fighter` receives
// a value **other than undefined.**
'_fighterUpdated(fighter)'
],
_fighterUpdated: function(fighter) {
this.nickname = (this.fighter.nickname || '... the dude has no nickname!');
}
});
</script>
Next, binding properties to HTML. When you bind to HTML contents, such as with <div>{{property}}</div>, what Polymer (currently) does behind the scenes is bind property directly to div.innerText. Polymer also only checks the first two characters of innerText to see if it's a {{ or [[, and does not do anything if it doesn't find them.
The Polymer team is working to make binding more robust, but so far as I know they haven't announced any concrete plans or timelines. For the time being, the solution is as you've discovered, just wrap an inline binding in <span> =)
Problem: I have an auto binding template in my main index.html page. Inside the template I am using two of my custom elements. One element is the producer of some data and the other one is the consumer of that data. These custom elements expose published/declared properties for each other to use and bind to. I was able to do that in Polymer 0.5 fairly easily (an example shown below). How do I do the same in Polymer 1.0?
How I used to do in Polymer 0.5?
In Polymer 0.5 I used to data bind between published properties of two custom elements using curly brace syntax and then inside it used the auto node finding concept to directly bind to other element's published property. An example shown below,
<template is="auto-binding">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
I tried something similar in Polymer 1.0 as shown in the example below
<template is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
But it throws an error:-
Uncaught TypeError: Cannot read property '$' of undefined
You can't do $.* bindings inside the template in Polymer 1.0. Instead, either refactor or use computed functions.
In your situation, since selectedLabel and toolbartitle shares the same value, it is much better to simply bind them to the same property.
Also, attribute names that are declaratively passed in (through the element tag) need to be serialized, so selectedLabel becomes selected-label.
<body>
...
<template id="tpl" is="dom-bind">
<my-navigation selected-label="{{myLabel}}" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{myLabel}}" id="my_scaffold"></my-scaffold>
</template>
<script>
...
window.addEventListener("WebComponentsReady", function (e) {
document.querySelector("#tpl").myLabel = "Home";
...
});
...
</script>
</body>
There is probably a better way to do that, but you can try this:
<body>
<template id="app" is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
<script>
var app = document.querySelector('#app');
app.addEventListener('template-bound', function () {
console.log('Our app is ready to rock!');
});
window.addEventListener('WebComponentsReady', function () {
document.querySelector('body').removeAttribute('unresolved');
var my-navigation = document.querySelector('my-navigation');
// This will add the variable to the 'app' context (template)
app.selectedLabel = my-navigation.selectedLabel;
});
</script>
</body>
I'm trying to include classes based on parameters of a json, so if I have the property color, the $= makes the trick to pass it as a class attribute (based on the polymer documentation)
<div class$="{{color}}"></div>
The problem is when I'm trying to add that class along an existing set of classes, for instance:
<div class$="avatar {{color}}"></div>
In that case $= doesn't do the trick. Is any way to accomplish this or each time that I add a class conditionally I have to include the rest of the styles through css selectors instead classes? I know in this example maybe the color could just simple go in the style attribute, it is purely an example to illustrate the problem.
Please, note that this is an issue only in Polymer 1.0.
As of Polymer 1.0, string interpolation is not yet supported (it will be soon as mentioned in the roadmap). However, you can also do this with computed bindings. Example
<dom-module>
<template>
<div class$="{{classColor(color)}}"></div>
</template>
</dom-module>
<script>
Polymer({
...
classColor: function(color) {
return 'avatar '+color;
}
});
<script>
Edit:
As of Polymer 1.2, you can use compound binding. So
<div class$="avatar {{color}}"></div>
now works.
Update
As of Polymer 1.2.0, you can now use Compound Bindings to
combine string literals and bindings in a single property binding or text content binding
like so:
<img src$="https://www.example.com/profiles/{{userId}}.jpg">
<span>Name: {{lastname}}, {{firstname}}</span>
and your example
<div class$="avatar {{color}}"></div>
so this is no longer an issue.
The below answer is now only relevant to versions of polymer prior to 1.2
If you are doing this a lot, until this feature becomes available which is hopefully soon you could just define the function in one place as a property of Polymer.Base which has all of it's properties inherited by all polymer elements
//TODO remove this later then polymer has better template and binding features.
// make sure to find all instances of {{join( in polymer templates to fix them
Polymer.Base.join = function() { return [].join.call(arguments, '');}
and then call it like so:
<div class$="{{join('avatar', ' ', color)}}"></div>
then when it is introduced by polymer properly, just remove that one line, and replace
{{join('avatar', color)}}
with
avatar {{color}}
I use this a lot at the moment, not just for combining classes into one, but also things like path names, joining with a '/', and just general text content, so instead I use the first argument as the glue.
Polymer.Base.join = function() {
var glue = arguments[0];
var strings = [].slice.call(arguments, 1);
return [].join.call(strings, glue);
}
or if you can use es6 features like rest arguments
Polymer.base.join = (glue, ...strings) => strings.join(glue);
for doing stuff like
<div class$="{{join(' ', 'avatar', color)}}"></div>
<img src="{{join('/', path, to, image.jpg)}}">
<span>{{join(' ', 'hi', name)}}</span>
of just the basic
Polymer.Base.join = (...args) => args.join('');
<div class$="{{join('avatar', ' ', color)}}"></div>
<template if="[[icon_img_src]]" is="dom-if">
<img class$="{{echo_class(icon_class)}}" src="[[icon_img_src]]">
</template>
<span class$="{{echo_class(icon_class, 'center-center horizontal layout letter')}}" hidden="[[icon_img_src]]">[[icon_text]]</span>
<iron-icon icon="check"></iron-icon>
</div>
</template>
<script>
Polymer({
echo_class: function(class_A, class_Z) {
return class_A + (class_Z ? " " + class_Z : "");
},
As far as my Polymer knowledge goes I can
bind a function using the "on-*" syntax to a webcomponent method
bind a function available in the global window namespace using vanilla html js binding (using onClick="...")
But I want to bind a function (provided as property of datamodel objects) to the webcomponent template.
One sidenote : Moving the datamodel objects to the global javascript namespace (i.e. window.*) is not an option.
The example below does'nt work but reflects exactly my use case :
...
Polymer('x-foo', {
items : [
...,
{
label : "ExampleCommand",
action : function() {
// do something
}
}
...
]
})
...
<template>
<template repeat="{{item in items}}">
<paper-button onClick="{{item.action}}">
{{item.label}});
</paper-button>
</template>
</template>
...
one more question if someone has an idea how to solve the question above) : how can i provide additional arguments to function ?
Any help is appreciated :-)
I had to ask the team about this because it's kinda confusing. Declarative event "bindings" are not the same thing as a Polymer expression. Unfortunately, both event bindings and Polymer expressions use the {{ }} syntax, which implies they work the same. They don't. The scope of event bindings is the element itself, whereas as an expression is scoped to the model for the template instance.
In Polymer 0.8, I believe the syntax has changed, so event bindings no longer use {{ }}. Hopefully that will clear it up a bit.
To achieve the effect you want, you can define a method on the element, which looks at the event target, grabs its model, and calls the function you've defined.
<polymer-element name="x-foo">
<template>
<template repeat="{{items}}">
<button on-click="{{doAction}}">{{label}}</button>
</template>
</template>
<script>
Polymer({
items: [
{
label: 'ExampleCommand',
action: function() {
alert('hello world');
}
},
{
label: 'AnotherCommand',
action: function() {
alert('another command');
}
}
],
doAction: function(e) {
e.target.templateInstance.model.action();
}
});
</script>
</polymer-element>
Here's the example running on jsbin