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

3

6

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>
Cove answered 24/9, 2015 at 20:40 Comment(0)
K
11

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>
Kohl answered 24/9, 2015 at 23:37 Comment(0)
P
6

With Polymer 1.2.4 as documented here we can use fire method options and force a child node (while still inside a parent element) to fire (and listen first of course) an event:

this.fire('test', {
    user: {
        name: 'Marios',
        gender: 'male'
    }
}, {
    node: Polymer.dom(this.root).querySelectorAll('my-child-element'),
    bubbles: false
});

We fired a custom event from an element but the emitter is the my-child-element so in there we can attach a listener in the listeners object. We also prevent the event bubbling so this event won't move upwards following the parents elements path. A typical listener could be:

Polymer({
    is: 'my-child-element',

    properties: {
        ...
    },
    listeners: {
        'test': '_myHandler'
    },
    _myHandler: function(e) {
        var user = e.detail.user;
        ...
    }
});
Pantheas answered 7/2, 2016 at 18:51 Comment(1)
this works but what if there is a dom-repeat attached to the child-element? for example, <dom-module id="parent-element"> <template is="dom-repeat" it> <child-element></child-element> </template> </dom-module> in this situation, the querySelector will just fire the event for first element that it matches, and querySelectorAll will fire the event for all items at once. I want one at a time. Any idea?Bridgeport
C
3
@arthur in the Polymer Slack site says:

Events tend to go up the DOM tree. Going down, you can use a data binding or invoke a method.

Polymer Slack Site

Cove answered 24/9, 2015 at 21:28 Comment(1)
Can't access the link attached, could you paste any example if he has provided one. Thanks!Cresting

© 2022 - 2024 — McMap. All rights reserved.