Search code examples
javascripthtmlpolymerpolymer-2.x

Timing of template stamping in relation to connectedCallback


Problem Description

It seems there is a timing issue with Polymer (2.x) when querying for nodes embedded in a template element right after connectedCallback() has been called. I would expect that the first call of this.shadowRoot.querySelectorAll('h1') in the sample below should return all <h1> nodes within the template. The Mozilla Custom Element Doc states:

connectedCallback()

Called when the element is inserted into a document, including into a shadow tree

To my understanding, all templates should be already stamped out at this time.

As can be seen in the snippet, the first query returns an empty list. However, the nodes are returned if the query is delayed.

Is there anything I'm missing here?

Sample Code

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">

    <base href="https://polygit.org/components/">
    <script src="webcomponentsjs/webcomponents-lite.js"></script>

    <link rel="import" href="polymer/polymer-element.html">
    <link rel="import" href="polymer/lib/elements/dom-repeat.html">
</head>

<body>
    <dom-module id="x-elem">
        <template>
            <template is="dom-repeat" items="[[testItems]]">
                <h1>[[item]]</h1>
            </template>
        </template>
        <script>
            class XElem extends Polymer.Element {
                static get is() { return 'x-elem' }
                static get properties() {
                    return {
                        'testItems': {
                            type: Array,
                            value: function () {
                                return [1, 2, 3];
                            }
                        }
                    }
                }

                ready(){
                    super.ready();
                }
                
                connectedCallback() {
                    super.connectedCallback();
                    console.log("Number of nodes after connectedCallback: ", this.shadowRoot.querySelectorAll('h1').length);
                    var callback = () => { 
                        console.log("Number of nodes after timeout: ", this.shadowRoot.querySelectorAll('h1').length); 
                    }
                    setTimeout(callback, 100);
                }
            }

            customElements.define(XElem.is, XElem);
        </script>
    </dom-module>

    <x-elem></x-elem>
</body>

</html>


Solution

  • The connectedCallback does not imply the element has been rendered. You could use Polymer.RenderStatus.afterNextRender for that:

    connectedCallback() {
      super.connectedCallback();
      Polymer.RenderStatus.afterNextRender(this, () => {
        console.log('Number of nodes after connectedCallback: ', this.shadowRoot.querySelectorAll('h1').length);
      });
    }
    

    demo