Add event listener to Polymer Webcomponent by id

Viewed 705

I'm just starting to try using Webcomponents, and I'm running into an issue trying to add event listeners to elements in my custom webcomponent.

My template looks like this:

<dom-module id="quadimageview">
    <template>
        <template is="dom-if" if="[[!issingleview]]">
            <div id="gridViewContainer" class="grid-view">
                <div id="topLeftCell" class="grid-cell">
                    <select id="colorTL" class="color-select" value="{{TLColor::change}}">
                        <template is="dom-repeat" items="[[colors]]">
                            <option selected$="[[optionSelected(item.value, TLColor)]]" value="[[item.value]]">[[item.name]]</option>
                        </template>
                    </select>
                    <imageviewer id="gridTLImgViewer"
                                      definedscan="[[TLColor]]"
                                      viewerheight="100%"
                                      viewerposition="none"
                                      zoomcontrol>
                    </imageviewer>
                </div>
                <div id="topRightCell" class="grid-cell">
                    <select id="colorTR" class="color-select" value="{{TRColor::change}}">
                        <template is="dom-repeat" items="[[colors]]">
                            <option selected$="[[optionSelected(item.value, TRColor)]]" value="[[item.value]]">[[item.name]]</option>
                        </template>
                    </select>
                    <imageviewer id="gridTRImgViewer"
                                      definedscan="[[TRColor]]"
                                      viewerheight="100%"
                                      viewerposition="none">
                    </imageviewer>
                </div>

                <div id="bottomLeftCell" class="grid-cell">
                    <select id="colorBL" class="color-select" value="{{BLColor::change}}">
                        <template is="dom-repeat" items="[[colors]]">
                            <option selected$="[[optionSelected(item.value, BLColor)]]" value="[[item.value]]">[[item.name]]</option>
                        </template>
                    </select>
                    <imageviewer id="gridBLImgViewer"
                                      definedscan="[[BLColor]]"
                                      viewerheight="100%"
                                      viewerposition="none">
                    </imageviewer>
                </div>
                <div id="bottomRightCell" class="grid-cell">
                    <select id="colorBR" class="color-select" value="{{BRColor::change}}">
                        <template is="dom-repeat" items="[[colors]]">
                            <option selected$="[[optionSelected(item.value, BRColor)]]" value="[[item.value]]">[[item.name]]</option>
                        </template>
                    </select>
                    <imageviewer id="gridBRImgViewer"
                                      definedscan="[[BRColor]]"
                                      viewerheight="100%"
                                      viewerposition="none">
                    </imageviewer>
                </div>
            </div>
        </template>
    </template>
</dom-module>

And my script looks like this:

    <script>
        require("jquery-ui/ui/widgets/resizable");
        require("leaflet.sync");

        class Quadimageview extends Polymer.LightDomElement {

            constructor() {
                super();
                this.loadedViewers;

                this.TLColor = 'EngineeringBlue';
                this.TRColor = 'EngineeringGreen';
                this.BLColor = 'EngineeringYellow';
                this.BRColor = 'EngineeringRed';

                this.colors = [
                    { value: 'EngineeringBlue', name: 'FL-Blue' },
                    { value: 'EngineeringGreen', name: 'FL-Green' },
                    { value: 'EngineeringRed', name: 'FL-Red' },
                    { value: 'EngineeringYellow', name: 'FL-Yellow' },
                    { value: 'EngineeringBfBlue', name: 'BF-Blue' },
                    { value: 'EngineeringBfGreen', name: 'BF-Green' },
                    { value: 'EngineeringBfRed', name: 'BF-Red' },
                ];
            }

            static get is() { return 'quadimageview'; }

            static get properties() {
                return {
                    issingleview: {
                        type: Boolean,
                        value: false
                    }
                }
            } 

            ready() {
                super.ready();
                this.initialize();
            }

            optionSelected(value, match) {
                return value == match;
            }

            setupSync(viewer) {                
                console.log(this.loadedViewers);

                if (this.loadedViewers) {
                    this.loadedViewers.forEach(function (element) {
                        if (element.map && viewer.map) {
                            element.map.sync(viewer.map);
                            viewer.map.sync(element.map);
                        }
                    });

                    this.loadedViewers.push(viewer);
                }
                else {
                    this.loadedViewers = [ viewer ];
                }
            }

            initialize() {
                console.log('> Init TL...');
                this.$.gridTLImgViewer.addEventListener('ImageViewerLoaded', function () { this.setupSync(this.$.gridTLImgViewer); }.bind(this));

                console.log('>> Init TR...');
                this.$.gridTRImgViewer.addEventListener('ImageViewerLoaded', function () { this.setupSync(this.$.gridTRImgViewer); }.bind(this));

                console.log('>>> Init BL...');
                this.$.gridBLImgViewer.addEventListener('ImageViewerLoaded', function () { this.setupSync(this.$.gridBLImgViewer); }.bind(this));

                console.log('>>>> Init BR...');
                this.$.gridBRImgViewer.addEventListener('ImageViewerLoaded', function () { this.setupSync(this.$.gridBRImgViewer); }.bind(this));
            }
        }

        window.customElements.define(Quadimageview.is, Quadimageview);
    </script>

And my component breaks on 'addEventListener', with the error saying 'Cannot add property "addEventListener" of undefined. So it seems like the 'this.$.' isn't the right call. Any hints as to what I'm setting up incorrectly? Thanks!

2 Answers

As I commented above you need to change custom components name dash separated after there are two way to add an event listener. One is inside the compenent as shown below. Or this.addEventListener('image-viewer-loaded',this._ImageViewerLoaded) defined at attached or ready function. So below if an element rendered then its events defined as below.

<image-viewer id='gridBLImgViewer'
               definedscan='[[BLColor]]'
               viewerheight="100%"
               viewerposition='none'
               on-image-viewer-loaded='_ImageViewerLoaded'>
</image-viewer>


.....

_ImageViewerLoaded() {
   //Your code here
}

in your image-viewer components, you will need to assign a function that image loaded. Than add a custom event in order to fire an event at its parent.

_ImageViewerLoaded(){
      this.dispatchEvent(new CustomEvent('image-viewer-loaded', {detail: { option}}));

}

Almost everything in your element is wrapped in an if statement:

<template is="dom-if" if="[[!issingleview]]">

And, as you can read [here], "Nodes created dynamically using data binding (including those in dom-repeat and dom-if templates) are not added to the this.$ hash."

That object is just a "shortcut", but you can still query the local dom to find the element. Just use something like

this.shadowRoot.getElementById('elementId')

or

this.shadowRoot.querySelector('#elementId')

Update: also, at the moment you run initialize the elements might not be yet stamped into the page. Just to check, you can run the call in a timeout, just to delay it a few milliseconds:

  ready() {
    super.ready();
    setTimeout(() => {
      this.initialize();
    });
  }

Also I don't know what you are trying to do there, but I see you are using some jQuery widget. Be sure to check if it works with your setup, because all the libraries that query the DOM by using document. something are not really compatible with the shadowDom. But that is outside the scope of this question...

Related