How do you decouple Web Components?

Viewed 1644

I'm trying to work frameworkless, with pure javascript Web Components. I want my Web Components to be able to work stand-alone and be used on different sites, and yet I also want two components to be able to communicate. So they should be able to communicate without being tightly coupled.

Back when I did Angular, this was easy. I can pass objects to a component through a HTML attribute, and the component receives it as an object rather than a string. But in pure javascript, attributes are always strings. What is the right way to pass objects around, or otherwise make Web Components aware of each other and able to communicate?

2 Answers

With Web Components you can pass objects through attributes as you said, but you can also pass an object with a method, or throug a property (which is actually a setter method).

<my-component id="comp1"></my-component>
...
var myObject = { y:1, y:2 }
comp1.value = myObject     //via property
comp1.setValue( myObject ) //via method

Here is a sample app with two native V1 Web Components. <component-1> can talk to <component-2> because you supply an ID into <component-1> and that ID refers to the ID set on <component-2>.

This is similar to how the <label> tag work with its for attribute.

HTML

<component-1 link-id="c2"></component-1>
<hr/>
<component-2 id="c2"></component-2>

JS

// Class for `<component-1>`
class Component1 extends HTMLElement {
  constructor() {
    super();
    this._linkedComponent = null;
    this._input = document.createElement('input');
    this._input.addEventListener('focus', this._focusHandler.bind(this));

    this._button = document.createElement('button');
    this._button.textContent = 'Add';
    this._button.addEventListener('click', this._clickHandler.bind(this));
  }

  connectedCallback() {
    this.appendChild(this._input);
    this.appendChild(this._button);
  }

  static get observedAttributes() {
    return ['link-id'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      if (newVal === null) {
        this._linkedComponent = null;
      }
      else {
        this._linkedComponent = document.getElementById(newVal);
      }
    }
  }

  _clickHandler() {
    if (this._linkedComponent) {
      this._linkedComponent.value = this._input.value;
    }
  }

  _focusHandler() {
    this._input.value = '';
  }
}

// Class for `<component-2>`
class Component2 extends HTMLElement {
  constructor() {
    super();
    this._textArea = document.createElement('textarea');
    this._textArea.setAttribute('style','width:100%;height:200px;');
  }

  connectedCallback() {
    this.appendChild(this._textArea);
  }

  set value(newValue) {
    this._textArea.value += (newValue+'\n');
  }
}

customElements.define('component-1', Component1);
customElements.define('component-2', Component2);

<component-1> will only talk to <component-2> if there is a component with the ID that was provided to <component-1> through its link-id attribute.

Related