Search code examples
javascripthtmlpolymervaadinpolymer-2.x

Polymer 2.0 Access Element Inside of Nested Template by Id


Polymer provides access to elements by id via this.$['foo']. However, I find that I am unable to access elements by id that are in nested templates.

<template>
     <vaadin-dialog id="dialog" >
          <template>
     <work-order-new  id="workordernew" on-submitted="_onWorkOrderSubmitted"> </work-order-new>
          </template>
     </vaadin-dialog>
    </template>

In this situation this.$.dialog works but this.$.workordernew does not. Are you able to access elements inside of a nested template by id and if so how?

I also tried both of approches below and they also didn't work .

Polymer.dom(this.root).querySelector():

this.$$(selector)

I saw a lot of answers for this question but none of them solved my problem.


Solution

  • The issue you're facing is not strictly a Polymer issue, but more a reflection of what Vaadin is doing behing the scenes.

    When the vaadin-dialog element is created the actual contents of the template are copied in an other element - vaadin-dialog-overlay - rather than staying in the original element.

    The overlay element is inserted at the document level, so you can grab it querying for it:

    let vaadinOverlay = document.querySelector('vaadin-dialog-overlay');
    

    Once you have the overlay it's easy to get to your work-order-new element:

    let workOrder = vaadinOverlay.$.content.querySelector('#workordernew');
    

    The real question would be in this case: why are you trying to access your elements directly?

    Usually, when working with components, getting to the point when you need to directly access a component is usually a good indication that there is something broken in the design of the application.

    <base href="https://polygit.org/vaadin-dialog+vaadin+v1.0.0-alpha1/vaadin-button+vaadin+v1.0.0-alpha1/polymer+^2.0.0/components/">
    
    <script src="webcomponentsjs/webcomponents-lite.js"></script>
    
    <link rel="import" href="vaadin-dialog/vaadin-dialog.html">
    <link rel="import" href="vaadin-button/vaadin-button.html">
    
    
    <my-element></my-element>
    
    <dom-module id="my-element">
      <template>
        <vaadin-dialog opened>
          <template>
            <work-order-new id="workorder"></work-order-new>
          </template>
        </vaadin-dialog>
      </template>
    </dom-module>
    
    <dom-module id="work-order-new">
      <template>
        <h4>Work Order</h4>
        <input type="text" id="inputbox" value="Default Value">
      </template>
    </dom-module>
    
    <script>
      // Extend Polymer.Element base class
      class MyElement extends Polymer.Element {
        static get is() {
          return 'my-element';
        }
    
        constructor() {
          super();
        }
    
        connectedCallback() {
          super.connectedCallback();
    
          /**
           * Fetch the reference to the overlay.
           **/
          let vaadinOverlay = document.querySelector('vaadin-dialog-overlay');
          
          /**
           * Fetch the reference to the `work-order-new` element.
           **/
          let workOrder = vaadinOverlay.$.content.querySelector('#workorder');
    
          /**
           * Wait for the overlay to be setup, this is just to show
           * that we can now use the reference to the `work-order-new`
           * element.
           **/
          setTimeout(() => {
            let inputBox = workOrder.$.inputbox;
            inputBox.value = `Changed from my-element`;
          }, 1500);
        }
      }
    
      customElements.define(MyElement.is, MyElement);
    
      class WorkOrderNewElement extends Polymer.Element {
        static get is() {
          return 'work-order-new';
        }
    
        constructor() {
          super();
        }
    
        connectedCallback() {
          super.connectedCallback();
        }
      }
    
      customElements.define(WorkOrderNewElement.is, WorkOrderNewElement);
    </script>