Polymer 1.0 strange behaviour on properties - polymer

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> =)

Related

How to change parent element property from child element?

How can I let a child element change the value of a property in the parent element with the ability to observe that change in the parent element
<link rel="import" href="paper-tree-node.html">
<dom-module id="paper-tree">
<template>
<div>
<paper-tree-node id="root" data="[[data]]" actions="[[actions]]" on-click='_handlePaperCheck' chapterIds={{chapterIds}}></paper-tree-node>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'paper-tree',
properties: {
chapterIds: {
type: Array,
value: [],
notify: true,
observer: "_chapterChanged"
}
},
_handlePaperCheck: function (e) {
let element = e.target.parentElement
if (element.checked) {
this.push('chapterIds', parseInt(element.id.substr(2)))
// console.info(this.chapterIds);
} else {
var index = this.chapterIds.indexOf(element.id);
this.splice('chapterIds', index, 1)
// console.info(this.chapterIds);
}
},
_chapterChanged: function () {
console.log(this.chapterIds)
// this.$.root.chapterIds = this.chapterIds
}
})
noting that paper-tree-node is a child element hosts a paper-check inside it's template, the purpose of this is to harvest the clicked paper-tree-node id attr and push it to the chapterIds property.
Problem is that _chapterChanged wont fire when i click on any checkbox
I am attaching a sample project since this cannot be posted on somthing like jsbin, here is a gdrive zip folder for the project https://drive.google.com/file/d/1yCeXkZu8Yp-8GUgadGHIfeP5w5uyI12J/view?usp=sharing
You're using the right thinking, but not the entire way.
notify: true, should be stated in your child element paper-tree-node under the property chapterIds, and not under your paper-tree element. I made this mistake too when I began with Polymer.
Also, whenever Polymer sees camelCase variables, it assumes the variable contains dashes:
<paper-tree-node id="root" data="[[data]]" actions="[[actions]]" on-click='_handlePaperCheck' chapterIds={{chapterIds}}></paper-tree-node>
... should be ...
<paper-tree-node id="root" data="[[data]]" actions="[[actions]]" on-click='_handlePaperCheck' chapter-ids={{chapterIds}}></paper-tree-node>
... where I switched the property chapterIds to chapter-ids. I rarely use camelCase variables when creating a new element because this mistake is so easy to make.
You can do this with an event or with data binding.

Two-Way Binding Across Components in Polymer

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.

Polymer 1.0 - Binding css classes

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 : "");
},

Remove child element's attribute from Polymer js

I've a custom element which, among other things, has a core-input and a paper button in it.
When the element is created, the input is disabled, and I want to enable it when I tap the button.
I've tried several ways and can't access the input's attribute.
<paper-input-decorator label="Nombre de usuario" floatingLabel>
<input id="usernameinput" value="{{UserName}}" is="core-input" disabled />
</paper-input-decorator>
<paper-button raised id="edprobutton" on-tap="{{edbutTapped}}">EDITAR</paper-button>
What should I write in
edbutTapped: function () {
},
EDIT
So, I've learned that the problem was that my username input element was inside a repeat template, and that's bad for what I was trying to do. Now I'm trying to bind a single json object to my element, with no luck so far.
What I have right now:
In my Index page:
<profile-page id="profpage" isProfile="true" entity="{{profEntity}}"></profile-page>
<script>
$(document).ready(function () {
var maintemplate = document.querySelector('#fulltemplate');
$.getJSON('api/userProfile.json', function (data) {
var jsonString = JSON.stringify(data);
alert(jsonString);
maintemplate.profEntity = jsonString;
});
}
</script>
In my element's page:
<polymer-element name="profile-page" attributes="isprofile entity">
<template>
<style>
[...]
</style>
<div flex vertical layout>
<core-label class="namepro">{{entity.Name}}</core-label>
<core-label class="subpro">{{entity.CompanyPosition}}</core-label>
<core-label class="subpro">{{entity.OrgUnitName}}</core-label>
</div>
</template>
</polymer-element>
And my JSON looks like this:
{"Name": "Sara Alvarez","CompanyPosition": "Desarrollo","OrgUnitName": "N-Adviser"}
I'm asuming I need to "update" my element somehow after changing its entity attribute?
Try the following
<script>
Polymer({
edbutTapped: function () {
this.$.usernameinput.disabled = false;
}
});
</script>
The this.$ allows you to access controls defined in an elements and the usernameinput is the id you assigned to the input.
This can go below the closing tag of the element you are defining.
'disabled' is conditional-attribute.
So this will be the correct use of it:
<input id="usernameinput" value="{{UserName}}" is="core-input" disabled?="{{isDisabled}}" />
In the prototype:
//first disable the field, can be done in ready callback:
ready: function () {
this.isDisabled = 'true';
}
//set idDisabled to 'false' i.e. enable the input
edbutTapped: function () {
this.isDisabled = 'false';
},
OK this is going to be a long answer (hence why I am not entering this as an edit of my original answer). I've just done something which is functionally the same.
The first thing is this code;
$.getJSON('api/userProfile.json', function (data) {
var jsonString = JSON.stringify(data);
alert(jsonString);
maintemplate.profEntity = jsonString;
});
Polymer has a control called core-ajax - this as it's name suggests makes an ajax call. The other really nice thing is that it can be made to execute when the URL changes. This is the code from the project I've got.
<core-ajax id="ajax"
auto=true
method="POST"
url="/RoutingMapHandler.php?Command=retrieve&Id=all"
response="{{response}}"
handleas="json"
on-core-error="{{handleError}}"
on-core-response="{{handleResponse}}">
</core-ajax>
The auto is the bit which tells it to fire when the URL changes. The description of auto from the polymer documentation is as follows;
With auto set to true, the element performs a request whenever its
url, params or body properties are changed.
you don't need the on-core-response but the on-core-error might be more useful. For my code response contains the JSON returned.
So for your code - it would be something like this
<core-ajax id="ajax"
auto=true
method="POST"
url="/api/userProfile.json"
response="{{jsonString}}"
handleas="json"
on-core-error="{{handleError}}" >
</core-ajax>
Now we have the data coming into your project we need to handle this. This is done by making use of Polymer's data-binding.
Lets detour to the element you are creating. Cannot see anything wrong with the following line.
<polymer-element name="profile-page" attributes="isprofile entity">
We have an element called 'profile-page' with two properties 'isprofile' and 'entity'.
Only because my Javascript leaves a bit to be desired I would pass each property as a seperate entity making that line
<polymer-element name="profile-page" attributes="isprofile name companyposition OrgUnitName">
Then at the bottom of your element define a script tag
<script>
Polymer({
name: "",
companyposition: "",
OrgUnitName: ""
});
</script>
Now back to the calling (profile-page). The following code (from my project) has the following;
<template repeat="{{m in response.data}}">
<map-list-element mapname="{{m.mapName}}" recordid="{{m.Id}}" on-show-settings="{{showSettings}}">
</map-list-element>
</template>
Here we repeat the following each element. In your case you only have one entry and it is stored in jsonString so your template is something like this
<template repeat="{{u in jsonString}}">
<profile-page name="{{u.name}} companyposition="{{u.companyposition}}" OrgUnitName="{{u.OrgUnitName}}">
</profile-page>
</template>
Now we get to the issue you have. Return to your profie-page element. Nothing wrong with the line
on-tap="{{edbutTapped}}"
This calls a function called edbutTapped. Taking the code I gave you earlier
<script>
Polymer({
edbutTapped: function () {
this.$.usernameinput.disabled = false;
}
});
</script>
The only thing to change here is add the following code
created: function() {
this.$.usernameinput.disabled = true;
},
This is inserted after the Polymer({ line. I cannot see in your revised code where the usernameinput is defined but I am assuming you have not posted it and it is defined in the element.
And you should be working, but remember to keep your case consistent and to be honest I've not been - certain parts of Polymer are case sensitive - that catches me out all the time :)

How can i bind a dynamic function within a polymer component?

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