sending json obj to polymer element - javascript

I have no idea why is this happening!
i want to send an object to polymer element and then use it.
im using something like this for the element
<polymer-element name="post-thumb" attributes="post">
<template>
<article>
<span>this is a post thumb</span>
<h1>{{post.title}}</h1>
<p>{{post.body}}</p>
</article>
</template>
<script>
Polymer('post-thumb');
</script>
</polymer-element>
and after HTML import, i'm calling it like this:
<post-thumb post='{"title":"post title 1","body":"post body"}'> </post-thumb>
it's weird cause when i use {{post}} it'll bring back the whole {"title":"post title 1","body":"post body"} but when i use {{post.title}} polymer simply returns nothing!
what's wrong here!? :/

I think you need to hint to the polymer that the attribute is of object type, ie try
<script>
Polymer('post-thumb', {
created: function() {
this.post = {};
}
}
);
</script>

Related

How to dynamically populate iron-list elements

So I have an iron-list element for a user's data history. The iron-list is not part of a custom element. It is simply on the page. I want to populate once the user has successfully logged in. Perhaps it is just my inexperience with polymer, but there doesn't seem to be a straightforward way to do this. First attempt (simplified for reading, e.g. I don't actually use jquery, there's lots of error-handling code I'm omitting, etc):
<iron-list as="item" style='height: 100%;' id='history-list'>
<template>
<div style='min-height: 140px;'>
<ul>
<!-- various fields for each record as list items -->
</ul>
</div>
</template>
</iron-list>
<script>
//once user is logged in
var items = $.getJSON('userhistoryscript');
//setAttribute doesn't work either
document.getElementById('history-list').items = items;
</script>
I would swear this worked in an earlier version of Polymer. But it doesn't seem to work now, which is fine, but I need an alternative.
Some alternatives I've considered:
Have iron-ajax element in same DOM scope and set '
the URL once the user is logged in to trigger the
xhr request. I'm not sure whether or not that'd work.
Wrap the list in a custom element and use an
iron-meta-query per chrisW's answer.
Those options are terrible. I cannot believe there is no simpler way to accomplish this feat. How do I conditionally fetch data based on user input and dynamically add an iron-list to the page (or update one that's already there)? Is there really no API for this use case?
UPDATE
Thank you for your answers. Turns out that my original code actually works fine: it was actually a build process issue. For some reason iron-list did not get installed when I installed the project dependencies through bower. I took out the vulcanized import (which must not have contained a ref to iron-list either) and imported all the elements directly, then I got the 404 and figured out what had happened.
I think that for best practices, you should use this.$.historyList to refeer id on this element. Anyway, when you get data to populate iron-listyou should use this.set('items', data); An example using your element looks like:
<iron-list>
<template is="dom-repeat" items="{{data}}" as="history">
<!--history.property-->
</template>
</iron-list>
<script>
Polymer({
properties:{
data:{type:Array, value:[],}
},
_functionToSetDataWhenUserIsLoggedIn: function(data){
this.set('data',data);
}
});
</script>
Edit
An example of iron-list
<template is="dom-bind">
<iron-ajax url="data.json" last-response="{{data}}" auto></iron-ajax>
<iron-list items="[[data]]" as="item">
<template>
<div>
Name: <span>[[item.name]]</span>
</div>
</template>
</iron-list>
</template>
This example is using an ajax call that executes automatically and populates the iron-listwithout the need to create a customized element.
More about iron-list on:
https://elements.polymer-project.org/elements/iron-list
I didn't entirely understand your question. Hope this helps.
<iron-list items="[[data]]" as="item">
<template>
<div>
Name: <span>[[item.name]]</span>
</div>
</template>
</iron-list>
properties:{
data:{type:Array, value:[],}
},
// the attached function is automatically called
attached: function() {
// Use an iron meta in the element that you keep track in of login information
// or create an onLogin listener
var isLoggedIn = new Polymer.IronMetaQuery({key: 'isLoggedIn'}).value,
if (isLoggedIn) {
var jsonData = $.getJSON('userhistoryscript');
this.set('data',jsonData);
}
}
Side note, when access elements by ids in Polymer elements, make sure you do it this way:
this.$.elementId
or
Polymer.dom('#elementId')
Edit since you don't want to create a custom polymer element
Source Code
<template is="dom-bind">
<iron-list id="list">
</iron-list>
</template>
<script>
document.addEventListener('onLogin', function(event) {
var list = document.getElementById('#list');
var jsonDataObjects = $.getJSON('userhistoryscript');
for (var i = 0; i < jsonDataObjects.length; i++) {
var div = document.createElement('div');
div.textContent = jsonDataObjects[i].info; // change this line
list.appendChild(div);
}
});
</script>

Polymer 1.0 add other polymer elements inside

Hello what is the difference of doing adding a child polymer element like this:
<dom-module id="app-element">
<template>
<h1>Hello</h1>
<test-element></test-element>
</template>
<script>
Polymer({
is: "app-element"
});
</script>
</dom-module>
<app-element></app-element>
This works just fine.
The effect of adding html code (including other polymer elements) inside the app-element tag
<app-element>some html here</app-element>
like this:
<dom-module id="app-element">
<template>
<h1>Hello</h1>
</template>
<script>
Polymer({
is: "app-element"
});
</script>
</dom-module>
<app-element>
<test-element></test-element>
</app-element>
This ignores the test-element code. So in which cases can I add html code inside a polymer element? When will it be ignored? What would be the case where you want to add polymer elements inside other polymer elements inside the html code like this:
<app-element>
<test-element></test-element>
</app-element>
?? Thank you
In your first case you are using local dom, in the second light dom.
In local dom, the custom element that contains it is responsible for the content (in this case app-element). So the creator of the custom element decides the content of the local dom. In contrast, using light dom provides the user of the custom element with the option to specify the content. The creator of the custom element can specify where the light dom should go inside the custom element using the <content></content> tag. So to make your second example work you would need something like this:
<dom-module id="app-element">
<template>
<h1>Hello</h1>
<content></content>
</template>
<script>
Polymer({
is: "app-element"
});
</script>
</dom-module>
<app-element>
<test-element></test-element>
</app-element>
An example use case for light dom is the paper-dialog. Using light dom, the user of the dialog can decide the content of the dialog. For example, the specific buttons to use, the main content of the dialog, etc.
Have a look at this page in the documentation for more information on local and light dom.

Polymer 1.0 can't access elements within nested <template> element

I am using Polymer 1.0 and I am building a small accordion example. I have data binding to the accordion text fine, I just want to change the icon of the accordion when I click it.
Below is my code
<dom-module id="ab-accordion">
<template>
<iron-ajax
auto
handle-as="json"
on-response="handleResponse"
debounce-duration="300"
id="ajaxreq"></iron-ajax>
<template is="dom-repeat" id="accordion" items="{{items}}" as="item">
<div class="accordion" on-click="toggleParentAcc">
<div id="accordion_header" class="accordion__header is-collapsed">
<i class="icon icon--chevron-down"></i>
<span>{{item.name}}</span>
</div>
<div id="standard_accordion_body" class="accordion__body can-collapse">
<div class="accordion__content">
<content id="content"></content>
</div>
</div>
</div>
</template>
</template>
<script>
Polymer({
is: "ab-accordion",
//Properties for the Element
properties: {
accordian: Object,
childaccordions: Object,
// Param passed in from the element - Set if the accordion is open by default.
open: String,
data: String,
reqUrl: {
type: String,
value: "https://example.com/service.aspx"
},
},
ready: function () {
this.items = [];
},
attached: function () {
// Run once the element is attached to the DOM.
},
toggleParentAcc: function (event) { // Toggle the classes of the accordions
//This is where I want to toggle the class
this.$.accordion_header.classList.toggle('is-collapsed');
if (typeof event !== 'undefined') {
event.stopPropagation(); // Stop the click from going up to the parent.
}
},
handleResponse: function (e) {
this.items = e.detail.response.sports;
}
});
</script>
</dom-module>
Basically inside the toggleParentAcc function I want to toggle the class of the div with ID accordion_header. But I just get undefined or null.
I have tried the following two lines:
this.$.accordion_header // #1
this.$$('#accordion_header') // #2
How I access that element inside the dom-repeat?
UPDATE: I can't even access the elements within the when inside the attached function.
attached: function(){
this.$.accordion_header // This is null?!
this.$$('#accordion_header'); // this is also null!
}
https://www.polymer-project.org/1.0/docs/devguide/local-dom.html#node-finding
Note: Nodes created dynamically using data binding (including those in dom-repeat and dom-if templates) are not added to the this.$ hash. The hash includes only statically created local DOM nodes (that is, the nodes defined in the element’s outermost template).
I think it would be better if you'd use Polymer.dom(this.root) instead. Also I'd advice you to not use static IDs in dom-repeat as they are meant to be unique. Use classes instead.
Looks like you might be encountering Event Retargeting which happens when events "bubble" their way up the DOM tree. Read this documentation to learn more.
When I encountered this, I solved it by using something like:
var bar = Polymer.dom(event).path[2].getAttribute('data-foo');
inside my Polymer() function.
To figure it out in your case, you should go to the console and search the DOM tree / event log to locate your target. If you have trouble locating the correct area of the console, post a comment and I might be able to help further.
I eventually figured out a way of doing this without having to select elements in the nested template.
<template id="accord_template" is="dom-repeat" items="{{items}}" as="item">
<ab-accordion-row id="[[item.id]]" name="[[item.name]]" open="[[item.open]]">
</ab-accordion-row>
</template>
ab-accordion is another element, I just feed it the data and I can then change the classes based on the params.
<div id="accordion" class="accordion" on-click="toggleAccordion">
<div class$="{{getClassAccordionHeader(open)}}">
<i class="icon icon--chevron-down"></i>
<span>{{name}}</span>
</div>
<div id="standard_accordion_body" class$="{{getClassAccordionChild(open)}}">
<div class="accordion__content">
<content></content>
</div>
</div>
</div>
try with this.
toggleParentAcc: function (event) { // Toggle the classes of the accordions
//This is where I want to toggle the class
var header = event.target.parentElement;
Polymer.dom(header).classList.toggle('is-collapsed');
// rest of your code
}

Polymer 1.0 - Issue with displaying values inside template is="dom-repeat"

While migrating to Polymer 1.0 from 0.5 I have come across an interesting thing. Thought it might help others having similar problem.
I have an element where I am using <template is="dom-repeat" items="{{customers}}">...</template>. The problem I am facing is I have to place every single property binding inside a HTML element. The code below what I intended to write:
<template is="dom-repeat" items="{{customers}}">
<div>
{{item.name}}<br />
{{item.addr}}, {{item.addr2}}<br />
{{item.phone}}
</div>
</template>
But it is only displaying the value for {{item.name}}. The reason is other property bindings are not wrapped within separate HTML tags, they are not displaying at all!
I tried the following but didn't work either:
<template is="dom-repeat" items="{{customers}}">
<div>
<p>{{item.name}}</p>
<span>{{item.addr}} {{item.addr2}}</span>
</div>
</template>
Means, I put {{item.name}} inside a <p>...</p> tag and placed {{item.addr}} and {{item.addr2}} inside a single <span>...</span> tag.
Then I went on and put every single property binding wrapped by their own HTML tags like the following:
<template is="dom-repeat" items="{{customers}}">
<div>
<p>{{item.name}}</p>
<span style="display:block">{{item.addr}}, <span>{{item.addr2}}</span></span>
<span style="display:block;">{{item.phone}}</span>
</div>
</template>
and it works!!
I truly have no idea whether it is a bug of 1.0 or there is something I am doing wrong! If anybody knows the answer please help.
Thanks in advance
You're not doing anything wrong. With the introduction of Polymer 0.9 (and later 1.0) data-binding to the content of text nodes only works if you wrap everything into its own element.
See the Polymer documentation:
The binding annotation must currently span the entire content of the tag
So you have to remove all whitespace and other characters for it to work.
Example from the documentation:
<!-- this works -->
<template>
First: <span>{{first}}</span><br>
Last: <span>{{last}}</span>
</template>
<!-- Not currently supported! -->
<div>First: {{first}}</div>
<div>Last: {{last}}</div>
<!-- Not currently supported! -->
<div>
{{title}}
</div>
Edit
As of Polymer 1.2, the issue described in the question is no longer problematic / erroneous. Compound bindings now work, see release notes on the Polymer blog.
Just a heads up, for element attributes though you can use something like a helper function for string concatenation. Here's an example.
<my-foo fullname="{{computeFullName(firstname, lastname)}}">
Hi, my name is <span>{{firstname}}</span>.
</my-foo>
...
computeFullName: function(first, last) {
return first + ' ' + last;
}
And here's the link: https://www.polymer-project.org/1.0/docs/migration.html#data-binding
EDIT:
For node content as well, string concatenation can be done using computed properties (I call them helper functions). Here's an example,
<dom-module id="x-custom">
<template>
My name is <span>{{fullName}}</span>
</template>
</dom-module>
<script>
Polymer({
is: 'x-custom',
properties: {
first: String,
last: String,
fullName: {
type: String,
// when `first` or `last` changes `computeFullName` is called once
// (asynchronously) and the value it returns is stored as `fullName`
computed: 'computeFullName(first, last)'
}
},
computeFullName: function(first, last) {
return first + ' ' + last;
}
...
});
</script>
With Polymer 1.2 you example code will actually work. Binding annotations no longer need to span the entire tag.
Example:
<div>first name: [[name.first]] last name: [[name.last]]</div>
https://blog.polymer-project.org/releases/2015/11/02/release-1.2.0/
You'll want to use a computed property to combine values. Search for them on this page https://www.polymer-project.org/1.0/docs/devguide/properties.html

Polymer - dynamic template rendering

is it somehow possible to render the template in a polymer element dynamically at runtime or when some light-dom elements are available?
I have the following situation:
index.html
<mega-menu>
<span class="menuTriggerText">Open Menu</span>
<ul class="test">
<li>TEST1</li>
<li>TEST2</li>
</ul>
<ul class="test">
<li>TEST1</li>
<li>TEST2</li>
</ul>
</mega-menu>
in my polymer-element.html i want to do something as follows:
<template>
<template repeat="{{temp}}"><p>I want to try this</template>
</template>
Polymer('mega-menu, {
created:function(){},
ready: function(){
//getting markup from light-dom
this.temp = document.getElementsByClassName('test');
},
attached:function(){},
detached: function(){},
attributeChanged: function(attrName, oldVal, newVal){}
});
Now, my question is - how do i get this to work? do i need to bind something on my template? or do i need some kind of event-listener or observer on something?
Thanks, Gbeschbacher
This is precisely what content insertion points are for. You can select top-level light DOM markup to render at specific locations in the Shadow DOM. <content select=""></content> takes a CSS selector and grabs nodes from the light DOM.
Your example would be:
<polymer-element name="mega-menu" noscript>
<template>
<content select=".test"></content>
</template>
</polymer-element>
You should not need to pry into the light DOM like this for such a simple case, but for the sake of completeness, we can get your example working with a few important tweaks:
document.getElementsByClassName('test') looks for nodes in the entire document. This is not what you want. You only want children of <mega-menu>. Instead of document use this (e.g. this.querySelectorAll('.test')).
You're giving <template repeat="{{temp}}"> a NodeList. It won't be able to stamp that out. It expects an Array. Something like this.temp = [].slice.call(this.querySelectorAll('.test')); would work.
http://jsbin.com/balodowi/2/edit

Categories

Resources