Search code examples
javascriptpolymerdom-eventspolymer-1.0

Polymer 1.0: How to pass an event to a child-node element without using <iron-signals>?


This Stack Overflow answer suggests using <iron-signals> to broadcast an event down the DOM tree to a custom element.

Below, I ask a different question.

Question

How do I:

  • pass an event down to a direct child node (custom element)
  • from a parent (custom element)
  • without using <iron-signals>?

Code

This is what I have so far. But it doesn't work.

parent-element.html
<dom-module id="parenet-element">   
  <template is="dom-bind">
    <child-element></child-element>
    <paper-button on-tap="_handleTap"></paper-button>
  </template>
</dom-module>
<script>
  (function(){
    Polymer({
      is: 'parenet-element',
      _handleTap: function() {
        this.fire("my-event");
      }
    });
  })();
</script>
child-element.html
<dom-module id="child-element"> 
...
</dom-module>
<script>
  (function(){
    Polymer({
      is: 'child-element',
      listeners: {
        "my-event": "foo"
      },
      foo: function(){
        // Do stuff
      }
    });
  })();
</script>

Solution

  • You definitely can. Without iron-signals you've got three options (that I currently know of):

    1. Get the parent and have the child attach an event listener to the parent
    2. The parent can have the child fire the same event
    3. You mentioned that events only go up. You can then make the child element listen to the document firing that event (but I think this is bad)

    Here's an example

    <!doctype html>
    <html>
    
    <head>
      <base href="http://polygit.org/components/">
      <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
      <link href="polymer/polymer.html" rel="import">
    </head>
    
    <body>
      <dom-module id="parent-element">
        <template>
          <child-element></child-element>
          <button id="btn" on-tap="_fireParentEvent1">Fire 1!</button>
          <button id="btn" on-tap="_fireParentEvent2">Fire 2!</button>
          <button id="btn" on-tap="_fireParentEvent3">Fire 3!</button>
        </template>
      </dom-module>
      <dom-module id="child-element">
        <template>
          <style>
            :host {
              display: block;
            }
          </style>
          <span id="changeMe">Message</span>
        </template>
      </dom-module>
    
      <parent-element></parent-element>
      <script>
        (function registerElements() {
          Polymer({
            is: 'parent-element',
            listeners: {
              'event-two': '_attachToChild'
            },
            _attachToChild: function(e) {
              // the parent makes the child fire an event
              var childElement = Polymer.dom(this.root).querySelector('child-element');
              childElement.fire('event-two', e.detail);
            },
            _fireParentEvent1: function(e) {
              // the parent fires an event
              this.fire('event-one', {
                message: 'hello'
              });
            },
            _fireParentEvent2: function(e) {
              this.fire('event-two', {
                message: 'goodbye'
              });
            },
            _fireParentEvent3: function(e) {
              // the parent fires an event
              this.fire('event-three', {
                message: 'game over'
              });
            }
          });
    
          Polymer({
            is: 'child-element',
            listeners: {
              'event-two': '_handleEventTwo'
            },
            ready: function() {
              var parent = this.parentNode;
    
              // the child listens to the parent's event
              parent.addEventListener('event-one', function(e) {
                this.$.changeMe.innerHTML = e.detail.message;
              }.bind(this));
    
              // listen to the document level event (since events travel up)
              // but this option is difficult to control
              document.addEventListener('event-three', function(e) {
                this.$.changeMe.innerHTML = e.detail.message;
              }.bind(this));
            },
            _handleEventTwo: function(e) {
              this.$.changeMe.innerHTML = e.detail.message;
            }
          });
        })();
      </script>
    </body>
    
    </html>