Is it possible to get the elements that will be/were rendered from a <template repeat>?
I have a component called poly-list, implemented below:
<poly-list dataList="{{ data.in }}" id="inList"
style="overflow: hidden; min-width: 324px; display: inline-block;">
<template>
<div> <!-- on-click event here -->
<paper-input-no-error value="{{ [0] }}"
class="in-paper-input"
on-change="{{ inChanged }}" id="0"></paper-input-no-error>
<paper-input-no-error value="{{ [1] }}"
class="in-paper-input"
placeholder="Value" id="1"></paper-input-no-error>
</div>
</template>
</poly-list>
I then activate the template in the domReady callback:
this.template.model = this.data;
this.template.setAttribute('repeat', '');
I need a way to get each individual element that the template will put into the DOM. I need to do this in order to add an event listener to each component that is rendered. I also want this to be encapsulated in my poly-list components so components implementing poly-list will not need to setup the event itself.
I need to add the event to the top level element in the template repeat. In this case it is the div. I commented where I mean in the code above. Event bubbling could work but would not be reusable due to the fact that I do not know how for in the element will be that triggered the event thus making it impossible to say the top level element is always 1 parent element above as in the example above.
Is there an event that will return each element as it is rendered or something similar?
"Is it possible to get the elements that will be/were rendered from a template repeat?"
Yes, but only after Template Repeat has finished. Then you can select the elements by automatic node finding: this.$
There is an example here: How can I know that Template Repeat has finished?
Can't you just use Polymer's declarative event binding?
<poly-list ...>
<template>
<div on-click="{{ clickHandler }}">
<paper-input-no-error ...></paper-input-no-error>
<paper-input-no-error ...></paper-input-no-error>
</div>
</template>
</poly-list>
(with a method on your outer element's prototype called clickHandler)
With regards to bubbling, there probably is some way which you could get it to work.
Related
I read carefully these threads
Polymer 1 nested dom-if within dom-repeat not updating when data changes
how to dynamically append an element to dom-if in Polymer?
How to access content of dom-if inside a custom element?
that may have some relation with my question but I didn't manage find any clue if I can do what I want and how.
In my company, there are several flows, each one for each business flow and each step of the flow is a screen coded as a Polymer 1 web component. All them are warraped in a root Polymer component which defines the route.
A simple exemple would be:
my-root-component:
<dom-module id="my-root-component">
<template>
<first-obrigatiory-page which-route={aValueReturnedFromFirstComponent}></first-obrigatiory-page>
<template is="dom-if" if="[[_isTrueFunction(aValueReturnedFromFirstComponent)]]" restamp>
<second-page which-sub-route={aValueReturnedFromSecondComponent}></second-page>
<template is="dom-if" if="[[_isTrueFunction(aValueReturnedFromSecondComponentComponent)]]" restamp>
<third-page ></third-page>
</template>
</template>
</template>
<script>
Polymer({
is: 'my-root-component',
behaviors: [doesntMatterHere],
properties: {
The first dom-if works as expected but the second seems not be taken in account and my third-page component is never showed.
I checked and the equivalent for _isTrueFunction(aValueReturnedFromSecondComponentComponent) is returning true.
Does aValueReturnedFromFirstComponent really return anything, because you should declare the attribute as which-route="{{aValueReturnedFromFirstComponent}}" instead of using simple { }.
Does the whichRoute (note the camelCase) property in the first-obrigatiory-page element have the notify: true property so the variable actually sends back the updated value?
I usually set observers on variables whenever dom-ifs don't update so I can see if they really change or not, and then set the variables myself through the console with document.querySelector('my-root-component').set('aValueReturnedFromFirstComponent', true) so I can see that the dom-if really updates.
A workaround could be to use events, but what you've done should work.
At first, i know, there are many questions about iron-list. But mostly about editing items and not whole template inside iron-list..
My code is really extremely complicated and posting it is pointless. I am working on data-tables which are using iron-list. I have element called diamond-listing and inside this diamond-listing i have iron-list.
You can image this like: Parent element define <template> with some content inside it, and child element (diamond-listing) will render this template as a table
Of course diamond-listing is used multiple times in my application and always with different template. For example: page users have columns with userID, userName etc.. and on page stations there are columns stationID, address etc.. with different number of columns. Every pagea has it's own <template> which i am trying to propagate to diamond-listing. For example:
<diamond-listing as="user" id="permissionsTable" type="pagination" pagination-items-per-page="6" header-data="{{headerData}}" address="/user/" loading="{{loading}}">
<div id="test" slot="content">
<template>
<div class="diamond-row" on-tap="_openUrl" info$="/user/[[user.id]]">
<diamond-item text="{{user.username}}"></diamond-item>
<diamond-item text="{{user.partner.name}}"></diamond-item>
</div>
</template>
</div>
</diamond-listing>
What i managed to do is to make it work in shadow dom using <slot> and simply rewrite <template> inside <iron-list>, but here we are.. For example using Firefox, which doesn't support webcomponents, there isn't <template> as a child of <iron-list> (because there is no shadow-dom) so there is no way how to update <template> and render iron-list.
What i tried:
1) Find template inside iron-list and use removeChild and appendChild functions.
var test = this.querySelector("#test template");
this.$$("#diamondList").removeChild(this.$$("#diamondList template"));
this.$$("#diamondList").appendChild(test);
Without success.
2) Define in HTML empty iron-list without any template inside it. And then in javascript add template dynamically. Without success. ( iron-list is crying it requires template)
3) Create dynamically iron-list using document.createElement
var test = this.querySelector("#test template");
var list = document.createElement("iron-list");
list.appendChild(test);
list.as = this.as;
list.items = [{"username":"test","partner":{"name":"Test partner","id":1}}];
list.id = "diamondList";
result: same as 2) ...
Is there a way, how to update template which is used to render all items in iron-list?
Or create iron-list with defined template inside JS ?
Or somehow do it with dom-repeat ? I won't have more than 10 items in listing, since it's fully pagination listing. ( this is propably simplest solution, but i don't know how to render <template> for every iteration
Here is one general answer, don't know if it will work for your case:
In Polymer, recommended way of manipulating the DOM is by manipulating the data, not by removeChild or appendChild.
For example,
if you have list of users as: var users_array = [....];
create the iron-list as:
<iron-list date="users_array">
<template>
...
<template>
</iron-list>
adding and removing elements in users_array will affect the iron-list
immediately.
Use a dom-if or use hidden inside the iron-list.
<iron-list items="[[items]]">
<template>
<template is="dom-if" if="[[item.isType1]]">
<!-- item1 -->
</template>
<template is="dom-if" if="[[item.isType2]]">
<!-- item2 -->
</template>
</template>
</iron-list>
I am using the google-map element and I read about the .resize() trick to fix the size of a google-map element.
However I could not get it to work. Moreless, it doesn't make sense to me.
In my application:
only the first page which displays maps renders properly. Another page which render a map will not render properly.
if the first page rendered doesn't contain a map, if I go to another page with a map, the map renders properly.
That is why I do not understand how this can be related to the core-animated pages.
Live demo:
http://nicolasrannou.github.io/webapp-core/components/webapp-core/demo.html#welcome/home
All the "Contact" pages contain maps.
The maps are created after pulling data from a google doc, using a template:
<template repeat="{{row, rowIndex in rows}}">
<!-- location -->
<template if="{{ row.gsx$type.$t === 'location'}}">
<google-map latitude="{{+ row.gsx$latitude.$t}}" longitude="{{+ row.gsx$longitude.$t}}" showCenterMarker zoom="15">
</google-map>
</template>
</template>
Those "google-map" element are pretty far in the shadow dom and encapsulated in templates.
I tried to access then in the core-animated-prepare event without success:
transitionPrepare : function(){
window.console.log(document.querySelectorAll('google-map'));
}
Is there a good way to access an element inside a template, inside a shadow dom?
Thanks
On accessing inside a shadow dom, here is one citation from the docs:
...If the element is in another shadow tree deep within another element, you can't traverse into it easily. You can use .shadowRoot if you really need to poke around:
element.shadowRoot.querySelector('x-other-element')
.shadowRoot.querySelector('#something');
On maps that do not resize properly, I'll first look at the timing: .resize() should be done AFTER the animation is completed.
I have been trying create a polymer-element that generates a ul list based on an ajax request and then renders the "li" elements based on templates that can somehow be passsed in.
It's basically an attempt to make a polymer rebuild of the 'select2' library for autocompletion.
So, the base template I have so far looks like this:
<polymer-element name="auto-complete" attributes="url_base item_template">
<aj-ax id="xhr" url="{{url_base}}" params="{}" handle_as="json" on-ajax-response="{{handle_res}}" on-ajax-error="{{handle_err}}"></aj-ax>
<input id="eingabe" type="text" on-keyup="{{process_request}}" on-blur="{{hide_dropdown}}"/>
<div id="dropdown" hidden?="{{hide}}">
<ul>
<template repeat="{{i in items}}">
<li> i.text
<!--
the process_request handler makes the ajax request and sets
the "items" and un-hides the dropdown.
the above works, but I want to make it more generic so that
you can pass in a template that reads the item model such as
<template ref="{{item_template}}" bind></template>
where item_template is the ID of a template in some outside
scope
-->
</li>
</template>
</ul>
</polymer-element >
</div>
I've also tried to make a base auto-complete.html polymer-element and then extend it based on the auto-complete type...but to no avail.
Any thoughts, ideas?
I want to stick to declarative methods if possible and avoid having to build the DOM elements myself with document.createElement
Is that even possible?
thanks!
I've come up with a cool approach to do this actually!
http://jsbin.com/hesejipeha/2/edit
The main idea is that you conditionally render the your template once you've injected any child templates into the shadow DOM (and thus made it possible to call them by ref in scope!)
I'm using one of the core polymer components that basically has:
<polymer-element attributes="label">
<div>{{label}}</div>
as part of the source. I'd like to inject some HTML into this so that it ultimately renders as:
<div>Item <small>Description</small></div>
Is there any way to do this without copying the entire component (which is basically impossible considering the dependency chain)?
Polymer doesn't allow setting HTML inside {{}} expressions because it's a known XSS outlet. However, there are ways around it (1, 2).
I'm not sure there's a great way around this issue but I found something that works. You want to extend the element but also need to modify its shadow dom because of the .innerHTML limitation. Taking paper-button as an example, it has an internal {{label}}. You could extend the element, drill into its shadow dom, and set .innerHTML of the container where {{label}} is set. React to label changing (labelChanged) and call this.super():
<polymer-element name="x-el" extends="paper-button">
<template>
<shadow></shadow>
</template>
<script>
Polymer('x-el', {
labelChanged: function() {
// When label changes, find where it's set in paper-button
// and set the container's .innerHTML.
this.$.content.querySelector('span').innerHTML = this.label;
// call paper-button's labelChanged().
this.super();
}
});
</script>
</polymer-element>
Demo: http://jsbin.com/ripufoqu/1/edit
Problem is that it's brittle and requires you to know the internals of the element you're extending.