How to add custom listener to Polymer View

Hi Guys,

I have a polymer view with a polymer child element - which pops up in a vaadin dialog. I’d like to be able to add save and cancel listeners to this popup editor form in javascript.

I’ve got the custom events bubbling up OK, but right now I have to do a custom query to get a reference to the element - which requires the vaadin dialog be opened and then closed as before then it is not found by a query.

This would all be solved if I could just put the listener in like I would with a text field.

Snippet from master view - defining the popup.

<vaadin-dialog id="operationDialog" style="width: 100%; height: 100%;">
<template>
    <dor-operation-form id="operationForm" value="[[selectedOperation]
]" on-save="operationSaveClicked"></dor-operation-form>
 </template>
</vaadin-dialog>

The “on-save” part doesn’t work.

This is the child component

class DorOperationForm extends PolymerElement {

    static get template() {
        return html`
<style include="shared-styles">
                :host {
                    display: block;
                    height: 100%;
                }
            </style>
<dom-module>
 <div style="display: flex; align-items: center; justify-content: center; flex-wrap: wrap; margin:30px;">
  <span style="flex: 100%;" id="previousEntry">Previous Entry: 0600 to 0800 55-Coring From 0 to 16</span>
  <h3 style="flex: 100%;">Operation Detail:</h3>
  <vaadin-text-field id="holeNumberField" style="flex: 50%; padding-right:5px; padding-left: 5px;" required label="Hole Number:" value="[[selectedOperation.holeNumber]
]"></vaadin-text-field>
  <vaadin-time-picker id="startTimeField" style="flex: 25%; padding-right:5px; padding-left: 5px;" label="Start Time" required value="[[selectedOperation.sTime]
]"></vaadin-time-picker>
  <vaadin-time-picker id="endTimeField" label="End Time" style="flex: 25%; padding-right:5px; padding-left: 5px;" required value="[[selectedOperation.eTime]
]"></vaadin-time-picker>
  <vaadin-combo-box id="operationCodeField" style="flex: 100%; padding-right:5px; padding-left: 5px;" required label="Operation Code" value="[[selectedOperation.code]
]"></vaadin-combo-box>
  <vaadin-text-field id="commentField" label="Comment" style="flex: 50%; flex-basis:100%; padding-right:5px; padding-left: 5px;" value="[[selectedOperation.comment]
]"></vaadin-text-field>
  <vaadin-number-field id="fromField" style="flex: 10%; padding-right:5px; padding-left: 5px;" label="From (m)" value="[[selectedOperation.metersFrom]
]"></vaadin-number-field>
  <vaadin-number-field id="toField" label="To (m)" style="flex: 10%; padding-right:5px; padding-left: 5px;" value="[[selectedOperation.metersTo]
]"></vaadin-number-field>
  <vaadin-number-field id="recoveryField" label="Recovered (m)" style="flex: 10%; padding-right:5px; padding-left: 5px;" value="[[selectedOperation.metersRecovered]
]" on-change="recoveryChanged"></vaadin-number-field>
  <vaadin-number-field id="recoveryPercentageField" label="Recovery (%)" style="flex: 10%; padding-right:5px; padding-left: 5px;" value="[[selectedOperation.recoveryPercentage]
]"></vaadin-number-field>
  <vaadin-button theme="primary" style="flex-basis: 100%; width: 25%; margin-top: 25px; margin-bottom: 25px;" on-click="saveClicked">
    Save 
  </vaadin-button>
  <vaadin-button style="flex-basis: 100%" on-click="cancelClicked">
    Cancel 
  </vaadin-button>
 </div>
</dom-module>
`;
    }

    static get is() {
        return 'dor-operation-form';
    }

    static get properties() {
        return {
            // Declare your properties here.
        };
    }

    connectedCallback() {
        super.connectedCallback();
    }

    saveClicked(){
        console.warn("Save Clicked")
        var event = new CustomEvent('save', {bubbles: true, composed: true})
        this.dispatchEvent(event);
    }

    cancelClicked(){
        console.warn("Cancel Clicked")
        var event = new CustomEvent('cancel', {bubbles: true, composed: true})
        this.dispatchEvent(event);
    }
}

customElements.define(DorOperationForm.is, DorOperationForm);

I can’t seem to find how to define the on-save listener that comes up to the HTML level element.
If I add the listener like this, it works.

//Inside connectedCallback() of parent element.

this.$.operationDialog.opened = true

        let vaadinOverlay = document.querySelector('vaadin-dialog-overlay');
        let operationForm = vaadinOverlay.$.content.shadowRoot.querySelector('#operationForm');
        let ref = this
        operationForm.addEventListener('save', function(e){
            ref.operationSaveClicked()
        })

        this.$.operationDialog.opened = false

Referencing the id of the child element itself never works, hence doing it via the vaadin dialog overlay.

Since the save event bubbles up and out of shadow roots you could add the save listener on body.

document.body.addEventListener('save', function(e) { ... });

However that might not be very optimal if you would have many different dialogs triggering events with the same name. You could add some scoping prefix to the event name though.

Another option would be to define the dialog contents using the renderer API of the dialog instead of a <template> element.

Or you could add an opened-changed event handler on the dialog itself (on-opened-changed="" if using binding) and then attach the listeners in there so they would be added lazily only when dialog is opened without explicitly opening and closing the dialog if not needed.

Hi Kari,

Thanks for the feedback - I do indeed have multiple editor windows that would all have a save listener.

What I really want to know is how to wire the listener via the HTML setup.

e.g

<dor-operation-form on-save="operationSaveClicked"></dor-operation-form>

I can’t seem to get the listener to wire up like this - I must be missing something.

I just looked at the older version of the dialog demos (which still use Polymer template in most examples) https://cdn.vaadin.com/vaadin-dialog/2.1.0/demo/#dialog-basic-demos

See the “Confirmation Dialog” demo at above URL, it basically does what you want. It has on-click="closeDialog" on buttons in the dialog template so I see no reason why on-save or any other event listener binding wouldn’t work.

Though the example uses [<dom-bind>]
(https://polymer-library.polymer-project.org/2.0/docs/devguide/templates#dom-bind) but that’s only because they were written with the use case in mind that you could just paste that in an HTML document (so it works outside a Polymer element template too). Afaik it should work just as well without dom-bind as long as you put the code inside the template of a Polymer element (e.g. your “master view” if it’s a Polymer element) and make sure that host element has the method with the correct name to handle the event (operationSaveClicked()).

If it still doesn’t work for you, try if you can make e.g. the on-click work for a button in the dialog. There could be some typo somewhere. Or I’m not sure if save event name can be somehow special, you could try renaming the event to see if it makes a difference.