How to access elements within dom-repeat in Polymer 3.0? - polymer

I have the following piece of code:
<dom-repeat id="template" items="{{chats}}">
<template>
<template is="dom-if" if="[[item.opened]]">
<section id$="msg-[[index]]"></section>
</template>
</template>
</dom-repeat>
How do can I access section with id=msg-3 and set scrollTop to 999 upon being rendered?
I know that in polymer 1 the best practice was to bind to on attached and wait on a dom-repeat event with a debouncer this no longer works in polymer 3, however.

If you know the index number you can access the element with ;
let el = this.shadowRoot.querySelector('#msg-'+<index>);
Remember, if item not rendered due to your filter with dom-if, you can not acces this element.
EDIT
In order to show on top of the selected item;
let screenPosition = el.getBoundingClientRect();
window.scrollTo(screenPosition);
Demo

Related

is it possible nest/inner dom-if where the second dom-if depends on value oberseved from the first dom-if block

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.

dynamically edit template of iron-list

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>

Nested child dom-repeat not updating if parent dom-repeat data is updated

I have the following nested dom-repeat:
<firebase-query
id="query"
path="[[path]]"
data="{{parentItems}}"
app-name="myApp">
</firebase-query>
<template is="dom-repeat"
items="{{parentItems}}"
as="parentItem">
<template is="dom-repeat" items="{{_toArray(parentItem)}}">
<div>{{item.details}} </div>
</template>
</template>
When items are added/removed from parentItems, the nested child dom-repeat template is not re-rendering i.e. _toArray() is not called. Is this behaviour expected? How do I ensure that when parentItems changes, the nested template will also be updated? Thanks.
I don't have the details of how you add something in the parentItems property, but i assume you do something like this
this.parentItems.push(something)
Polymer won't see the array change in that case, try to use the polymer push instead,
this.push('parentItems', something)
It will push it in the array, and notify polymer binding to update the view.

Polymer Advanced DataBinding

I plan to display three columns in my application, each with roughly 1k of custom items (picture, header, text). It will probably work with something like
<template>
<template bind="{{myData as m}}">
<template repeat if="{{m.1}}">
<custom-item icon="{{m.icon}}" label="{{m.title}}"></custom-item>
</template>
<template repeat if="{{m.2}}">
<custom-item icon="{{m.icon}}" label="{{m.title}}"></custom-item>
</template>
...
</template>
</template>
but I want to avoid loading 3k items from database to client, they will probably stay within top 200. Is there a way to do some sort of dynamic loading in polymer?
i would recommend core-list for this. https://www.polymer-project.org/docs/elements/core-elements.html#core-list i recently used it in a album list. (large album covers and several buttons and text) it greatly improved performance from using only a repeating template. to load more results you could use a onscroll callback.
keep in mind all this is assuming you are working in a auto-binding template.
<template id="app" is="auto-binding">
// all html code in here
</template>
and waited for the template to be bound
document.querySelector("#app").addEventListener('template-bound', function () {
//all this js code will be in here
});
im my app i used core-header-panel to get scroll event it would work the same with core-scroll-header-panel. i gave the header panel a id. i just called it headerPanel.
<core-header-panel id="headerPanel">
then i set the scrollTarget for core-list to use the scroller of the headerPanel. make a variable in js
this.scrollTarget = document.querySelector('#headerPanel').scroller;
then i link that to the core-list
<core-list data="{{data}}" scrollTarget="{{scrollTarget}}">
<template>
<custom-item icon="{{model.icon}}" label="{{model.title}}"></custom-item>
</tempalte>
</core-list>
that has core-list set up. as for not loading 3000 results i would use a callback on the scroller we set up a min ago. then calculate a % of the page that has been scrolled and call a function if scrolled more then say 80%
this.scrollTarget.onscroll = function () {
var precent = (this.scrollTarget.scrollTop / (this.scrollTarget.scrollHeight - this.scrollTarget.offsetHeight)) * 100;
if (precent > 80) {
// call some function to .push() more to this.data
}
};
edit: added bit about waiting for template to be bound.
hope this helps.
There is also a polymer element for infinite scrolling :
core-scroll-thresold, you can combine it with the core-list

Polymer document.querySelector not working as expected

Either I am doing something horribly wrong or Polymer just doesn't like me. See following:
<polymer-element name="menu-paper-ui" noscript>
<template>
<paper-dialog heading="Dialog" transition="paper-dialog-transition-bottom">
[ .. ]
</paper-dialog>
<paper-button label="Dialog Bottom" on-tap="{{toggleDialog}}"></paper-button>
</template>
<script>
Polymer('menu-paper-ui', {
toggleDialog : function() {
var dialog = document.querySelector('paper-dialog');
console.log(dialog); //returns null
dialog.toggle();
}
})
</script>
</polymer-element>
Now, I have my reasons to use querySelector. So, if someone can tell me whats going wrong that will be great!
This question is nearly identical to Using querySelector to find nested elements inside a Polymer template returns null.
The short answer is that elements in a polymer-element's template are put into the ShadowDOM of that element, are not not visible to the anything outside of that element. This is so that you can control styling more easily, and element IDs are scoped.
You can either give the dialog an id and use Polymer's automatic node finding, or use this.shadowRoot.querySelector('paper-dialog').
The Problem is that you can not access the shadow DOM inside a custom element with document.querySelector. See my answer to a similar question.