Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you decouple Web Components?

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?

like image 264
mcv Avatar asked Mar 08 '23 11:03

mcv


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
like image 56
Supersharp Avatar answered Mar 10 '23 02:03

Supersharp


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.

like image 34
Intervalia Avatar answered Mar 10 '23 02:03

Intervalia