Polymer - dynamic template rendering - javascript

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

Related

How can I dynamically add items into paper-dropdown-menu?

I tried adding it with like dropdownMenu.appendChild(menuItem) but as I expected this doesn't work. I couldn't find information about this on Polymer's guides nor other similar questions on here.
Is that possible? If so, how?
paper-dropdown-menu: https://elements.polymer-project.org/elements/paper-dropdown-menu
In Polymer, recommended way of manipulating the DOM is by manipulating the data:
put the list of menu items in array: var items_array = [....];
-create the menu as:
<paper-dropdown-menu label="Your favourite pastry">
<paper-listbox class="dropdown-content">
<template is="dom-repeat" items="{{items_array}}">
<paper-item>{{item}}</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
adding and removing elements in items_array will affect the menu immediately.
Found out the proper way from their docs:
Should select the Polymer element with: Polymer.dom(parent).querySelector(selector)
And append with: Polymer.dom(parent).appendChild(node)

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 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
}

Data binding (AngularJS) inside JS

I'm trying to use data binding inside a <script>.
<ol class="breadcrumb"></ol>
<div class=heading><h2>{{current.name}}</h2>
...
<script>
$(function(){
$(".breadcrumb").append('<li> Ledige stillinger</li>');
$(".breadcrumb").append('<li><a href=#!/categories/{{current.keyname}}>{{current.name}}</a></li>');
});
</script>
</div>
My result is:
Ledige stillinger (this is ok!) / {{current.name}} (here should it be the name, it works inside h2)
What am I doing wrong?
my answer would be to use AngularJS as it is intended and 1-way bind your DOM to the model values.
if there are multiple elements with the "breadcrumb" class then add these to a collection in your controller's scope and use ng-repeat directive to render them all out then append the "li" tags to this template.
Please see this plunkr example: http://plnkr.co/edit/0T6ldRzSTCnVIKnn3WRh
look at app.js and the following markup:
<ul class="breadcrumb">
<li>{{current.name}}</li>
</ul>

Highlight reactive elements when they are inserted into the DOM - Meteor

I want to know how I can highlight new elements with $('.game-panel').effect('highlight'); (jQuery UI) immediately after they are inserted into the DOM.
{{#each games}}
{{> game}}
{{/each}}
<template name="game">
<div class="game-panel">
Test
</div>
</template>
Any help would be greatly appreciated.
You could try using the rendered callback.
Template.game.rendered=function(){
this.$('.game-panel').effect('highlight');
};
There is also a yet undocumented API called "UI hooks" which theorically could allow finer grained control over when elements get inserted in the DOM.
Announcement on meteor-core :
https://groups.google.com/forum/#!msg/meteor-core/1kUoG2mcaRw/j4bNvXu36IoJ
Example of use :
https://github.com/percolatestudio/transition-helper/blob/master/transition-helper.js

Categories

Resources