Webcomponents communicating by custom events cannot send data

691 Views Asked by At

I'm intrigued by webcomponents (and pretty new to javascript) and I am trying to set up a dummy microfrontend app. Ok, components are served by different web server (webpack devserver to be honest) and correctly displayed in the 'aggregator' page. I also add a customevent to send some text from a webcomponent to another and this is not working. Here is the sender webcomponent:

const tmplt = document.createElement('template');
tmplt.innerHTML = `
<div>
  <label for="sendnpt">message:</label>
  <input id="sendnpt" type="text"></input>
  <button id="sendbtn">Send the message</button>
  <div id="msglog"></div>
</div>
`;

var input = {};

window.customElements.define('team-zero', class TeamZero extends HTMLElement {
  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ 'mode': 'open' });
    this._shadowRoot.appendChild(tmplt.content.cloneNode(true));
    this.button = this._shadowRoot.getElementById('sendbtn');
    input = this._shadowRoot.getElementById('sendnpt');
    this.button.addEventListener('click', function(evt) {
      var msg = {
        bubbles: true,
        detail: {}
      };
      msg.detail.name = input.value;
      console.log('sending msg: ' + JSON.stringify(msg))
      window.dispatchEvent(new CustomEvent('greet', msg));
    });
  }
  connectedCallback() {
    console.log('connected and ready!');
  }
});

And this is the receiver:

const m = require('mithril');
window.customElements.define('team-one', class TeamOne extends HTMLElement {
  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ 'mode': 'open' });
    window.addEventListener('greet', (msg) => {
      console.log("a message arrives: " + JSON.stringify(msg));
    });
  }
  connectedCallback() {
    console.log('connected!');
    m.mount(this._shadowRoot, {
      view: function(vnode) {
        return m("div", "team-0ne runs mithril!");
      }
    });
  }
});

Problem is that event is emitted and received, but there is no detail in it. Why receiver component cannot log the text the other one is sending, what am I missing?

You can see the whole dummy project here.

1

There are 1 best solutions below

3
On BEST ANSWER

Unlike default Events like click,
Custom Events require a composed:true to 'escape' shadowDOM

Note: besides data you can also pass Function references

<my-element id=ONE><button>Click One</button></my-element>
<my-element id=TWO><button>Click Two</button></my-element>

<script>
window.customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'}).innerHTML = `<slot></slot>`;
      
      let report = (evt) =>
        document.body.append(
          document.createElement("br"),
          `${this.id} received: ${evt.type} - detail: ${JSON.stringify(evt.detail)}  `,
          evt.detail.callback && evt.detail.callback(this.id) // not for 'click' event
        );
      
      window.addEventListener('greet', report); // Custom Event
      window.addEventListener('click', report); // button click
        
      this.onclick = () =>
        window.dispatchEvent(new CustomEvent('greet', {
          bubbles: true,
          composed: true,
          detail: {
            fromid: this.id,
            callback: this.callback.bind(this)
          }
        }));
    }
    callback(payload){
      return `${this.id} executed callback function(${payload})`;
    }
  });
</script>

Also see: https://pm.dartus.fr/blog/a-complete-guide-on-shadow-dom-and-event-propagation/