1
votes

Background

I've been working with Polymer for a while. I've been converting from .5 and building new elements for a production app. We are currently using Polymer 1.0.6, and this particular issue is also using jQuery 2.x.x and typeahead.js.

Issue

We have an element that builds a dynamic list of label and inputs provided by a data source. in the ready function we get a list of input data, and set that to a local list variable that is bound to a foreach template to create the labels and inputs.

I was unable to find a Polymer element I really liked for typeahead, for Polymer 1.0. So I defaulted to using typeahead.js. my problem is that I cannot find a lifecycle event or workaround, to call the typeahead function after the dom has processed setting the bound list in the ready function.

Code

The easiest way to demonstrate this issue, was to create a HEAVILY trimmed down version in a jsbin. I know the element looks bad, it was cut down as much as possible to demo the core issue I'm facing.

http://jsbin.com/zivano/edit?html,output

What Have I Tried?

I've tried using the attached event, and while it does process after the ready function, the dom changes from ready have not taken effect. I found similar issues on SO domReady vs ready - Migrating to Polymer 1.0 I've tried both suggestions, the second is still being used in the jsbin, without success.

I have also bound the click event of my inputs to a function calling the typeahead setup code, to prove that if the calls are made after the dom is rendered it will work correctly.

Summary

If update a data bound, local variable in the ready function, is there a lifecycle event I can call that will guarantee that those dom changes will be rendered, so I can make a dom query against those new items? Or is there a work around that will let me call a js function on a dom element, one time after the element dom fully renders?

2
Unless you are planning to stay with Polymer's Shady DOM for a long time, I'd advice you to not use jquery as it can't traverse inside the Shadow DOM. But to be safe you can wrap an element returned by Polymer.dom(this.root).querySelector in jquery. Also don't use numerical IDs as they are invalid. - Neil John Ramal
@NeilJohnRamal great comment on wrapping the Polymer.dom call. I had originally tried using Polymer.dom, but it wouldn't accept the .typeahead as a method. Wrapping it did the trick. also the jsbin was admittedly a hack job, just to throw an example up there. I have changed it, to use normal string IDs. - Mabdullah
Try wrapping your initialization code in the attached callback with this.async(function () {...});. You are creating a nested template (dom-repeat inside the main element's template) - Polymer only guarantees that by the time attached is called, default values set, light dom is attached and stamped into main doc, but your shadow dom might still be rendering and inside the microtask queue. async ensures that you call your initialization code after. - zerodevx
@zerodevx lost my mind for a second, i had tried your solution in my element, and it had failed. When i try it with the jsbin however, it works great. The difference is that the real element loads data from an ajax call. Meaning the ajax call may not, and in fact does not complete before the async call in the attached method. that being said i appreciate the comment, it made me re-evaluate the race conditions, I got it to work using promises, to guarantee they typeahead init is called after the .done function of the ajax. - Mabdullah

2 Answers

1
votes

my problem is that I cannot find a lifecycle event or workaround, to call the typeahead function after the dom has processed setting the bound list in the ready function.

I think I had a problem like this . For my problem I found a solution using the following :

var self = this;
window.addEventListener('WebComponentsReady', function(e) {
    // imports are loaded and elements have been registered
   /*Example*/
    console.log('Components are ready');
    var p = self.getElementsByTagName("paper-item");//paper-item created dynamically
    console.log(p);//can access and use this paper-item 
   /*Finish example*/
   //here you can call typeahead because the dom has been processed
});

Sorry for my English or if I dont understand your question, my English is bad.

1
votes

The Issue I had was that the data-bound list was populated through an ajax function, which was completed after the attached function, even if I made an async call inside of the attached function, it would still fail because of race conditions.

It's worth noting the answer by Flavio Ochoa, will work. I personally preffered to not have my custom elements add listeners to the Window. So i went a different route.

Since my issues we're predicated on guaranteeing that the bound list was updated, I wrapped the ajax call in a Promise, and added the typeahead init logic to the then statement. That solution appears to be working.

I do have some concerns whether the promise can guarantee that the bound list will have propagated to the DOM by the time the then statement is processed. But so far it has worked consistently. I'll edit this answer if I can prove otherwise.