Polymer 1.0: How to pass an event to a child-node

2019-03-20 13:04发布

问题:

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>

回答1:

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>



回答2:

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;
        ...
    }
});


回答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