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

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)

Related

Is it possible to make a Web Component List with undetermined number of slot?

I know it can be achieved using Javascript by manually separate the slot elements and put them into the DOM tree but I wonder if it's supported within the box. Something like this:
<my-element>
<div slot="items">Item 1</div>
<div slot="items">Item 2</div>
<div slot="items">...</div>
</my-element>
And the template should be like this:
<div class="items">
<div class="item"> <!-- Should be 1 item per slot -->
<p>Something else</p>
<slot name="items"></slot>
</div>
</div>
Is this only possible using slotchange and populate the items by Javascript? Is there any better solution, without Javascript or with less Javascript? Maybe there is an element similar to slot that I am not aware of?
since all slot="items" will be slotted into <slot name="items"> only with the slotchange Event can the Component Author determine what happened.
Note: You do not populate the items, the default slotting mechanism does; you can only remove items after they are slotted; but then your HTML above needs more info on which Item needs to be slotted.
Then the question remains, Why use multiple slot="items" at all then?? If a slot can take 1 item, then only assign 1 item
Imperative Slots might help out: https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Imperative-Shadow-DOM-Distribution-API.md
Update, I misunderdstood OPs question
All he needs to do is:
Create another Web Component: <slotted-item slot="items"></slotted-item>

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 modeling template within template

I'm trying to create dropdown select (appearing if radio button is selected) in Polymer, that triggers another dropdown select on-iron-select.
All of this lives within a parent template:
<dom-module id="likes-cars">
<template id="maintemplate">
<paper-radio-button checked="{{likesCars}}" id="thebutton">I like cars.</paper-radio-button>
<template is="dom-if" if="{{likesCars}}">
<paper-dropdown-menu label="Your favourite car make">
<paper-menu class="dropdown-content" on-iron-select="modelfunc">
<paper-item>Make 1</paper-item>
<paper-item>Make 2</paper-item>
</paper-menu>
</paper-dropdown-menu>
</template>
<template id="menutemp">
<paper-dropdown-menu label="Your favourite car model">
<paper-menu class="dropdown-content" >
<template is="dom-repeat" items="{{models}}"id="modelstemplate" >
<paper-item>{{item}}</paper-item>
</template>
</paper-menu>
</paper-dropdown-menu>
</template>
</template>
And my Polymer script once the iron-select occurs is:
<script>
Polymer({
is:"likes-cars",
modelfunc: function() {
this.$.menutemp.model={}
this.$.modelstemplate.model={models:["Model 1","Model 2"]}
}
});
</script>
This results in the error:
Uncaught TypeError: Cannot set property 'model' of undefined
What is the best way to select and model "modelstemplate" with an array passed in?
Do I need to model the template around it("menutemp") separately?
I think you're misunderstanding how Polymer's data binding works a bit.
When you bind to something using the [[property]] or the {{property}} notation in a custom element, you're binding to the custom element's properties, no matter if you actually define the properties when calling the Polymer() function.
So when you say your dom-if and your paper-radio-button are bound to likesCars and that the dom-repeat is bound to models they're bound to this.likesCars and this.models.
So all you need to do in your modelfunc function is to set this.models to an array of Strings you want to show as items for the second menu. With this considered, the template enveloping the second menu is actually unnecessary.
Here's a jsbin with a working example based on your code, I added a selectedMake property too so that you can actually set different arrays to the second menu depending on what was selected on the first menu.
I hope this helped

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

How do I reliably determine if parent template item empty or not in jquery template?

I'm using jQuery templates to build a tree. It works very well so far, but I came across an issue when trying to determine if an item is at the root level or not.
I use a template similar to that of render tree items:
<script id="tree-row-tmpl" type="text/x-jquery-tmpl">
<li>
<div class="row ${NodeType}">
${Name}
</div>
{{if expanded}}
<ul>
{{tmpl($data.chidren || []) "#tree-row-tmpl"}}
</ul>
{{/if}}
</li>
</script>
Now in the link click handler I try to determine root node using:
if($.tmplItem(this).parent)
It turned out the root tmplItem.parent is not null (as I expected), but contains an object with two properties: {data:{}, key:0}.
I see that I can check item.parent.parent or one of the properties which exist in regular tmplItem and missing in the root object. But this seems like a kind of hack for me - I'd prefer finding an "official" way of verifying a tmplItem whether it's empty or valid.

Categories

Resources