Polymer: this.$ is empty when elements are inside dom-if

Created on 30 Sep 2015  路  9Comments  路  Source: Polymer/polymer

I have a component that looks something like this:

<template>
    <template is="dom-if" if="{{userLoggedIn}}">
        <div class="logged-in-wrapper" id="loggedInWrapper">
            <content></content>
        </div>
    </template>
    <template is="dom-if" if="{{!userLoggedIn}}">
        <div class="logged-out-wrapper" id="loggedOutWrapper">
            <content></content>
        </div>
    </template>
</template

In the ready function, this.$ is empty, even though there are two elements that have ids. When I removed the wrapping <template is="dom-if"..., this.$ was populated with the expected ID'd elements. I'm running polymer 1.1.3.

Most helpful comment

Any DOM defined in your <dom-module>'s <template> is local DOM to that element. It's the element's shady/shadow dom.

ready() is a lifecycle method that signifies when your element's shady/shadow dom has been created, properties init'd, etc.

The problem here is that content inside a <template if> is inert until it's stamped and Polymer cannot see content inside the template until that happens. DOM manipulations happen at the end of a microtask, at the end of property changes. That's why you need to wait a tick (this.async) for the <template if> to have stamped its content. If you're doing this on page load, you could try:

ready: function() {
  this.async(function() {
    ...
  });
}

All 9 comments

This is a confusing bit for sure

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鈥檚 outermost template).

https://www.polymer-project.org/1.0/docs/devguide/local-dom.html#node-finding

basically this is the intended behavior

+1 to further thought around this

If I'm reading it right, I think this is what $$(selector) is good for.

See: https://www.polymer-project.org/1.0/docs/devguide/utility-functions.html

Thanks for the info! Looks like $$(selector) is indeed the workaround, so I will close this ticket.

I spoke too soon. this.$$ is also not working for templates with dom-if.

+1 for making this not the intended behavior as it is misleading.

Grr, you are right, immediately inside ready() it does not ... however ...

these will work:

this.querySelector('#myID');
this.$$('#myID');

if you put them in an async like:

this.async(function(){
    this.querySelector('#myID');
    this.$$('#myID');
}.bind(this), 50);

Ah okay, that does seem to work. Going to leave this ticket open though b/c this seems like an okay work-around but not an ideal scenario.

Thanks again!

Agreed +1

Also, I would like to know the definition of ready() as it pertains to nested templates.

I reread this: https://www.polymer-project.org/1.0/docs/devguide/registering-elements.html#ready-method but I don't see any information specific to this.

Are these nested templates considered local DOM or light DOM?

Any DOM defined in your <dom-module>'s <template> is local DOM to that element. It's the element's shady/shadow dom.

ready() is a lifecycle method that signifies when your element's shady/shadow dom has been created, properties init'd, etc.

The problem here is that content inside a <template if> is inert until it's stamped and Polymer cannot see content inside the template until that happens. DOM manipulations happen at the end of a microtask, at the end of property changes. That's why you need to wait a tick (this.async) for the <template if> to have stamped its content. If you're doing this on page load, you could try:

ready: function() {
  this.async(function() {
    ...
  });
}

@ebidel Thanks for making that clear :)

Was this page helpful?
0 / 5 - 0 ratings