1

I'm learning web components (native and with frameworks). Is it possible to inject properties into a native component like I can do in for example Vue.js?

This is my-component.js:

const templateComponent = document.createElement('template');
templateComponent.innerHTML = `<div>{{ test }}</div>`;

class MyComponent extends HTMLElement {

    connectedCallback() {
        this._container = this.querySelector("DIV");
    }

}

window.customElements.define("my-component", MyComponent);

This is index.html:

<!doctype html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>

<my-component test="Does this work?"></my-component>


<script src="my-component.js"></script>

</body>
</html>
Andreas
  • 430
  • 5
  • 21

1 Answers1

1

Yes and no.

There is nothing built in for templating. But that doesn't mean you can't do it.

function render(el) {
  el.shadowRoot.innerHTML = `<div>${el._test}</div>`;
}

class MyComponent extends HTMLElement {
  static get observedAttributes() {
    return ['test'];
  }

  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      this[`_${attrName}`] = newVal;
      render(this);
    }
  }
}

window.customElements.define("my-component", MyComponent);
<my-component test="Does this work?"></my-component>
<my-component test="Yes it does"></my-component>

You use observedAttributes to indicate which attributes you want to watch for changes. And you use attributeChangedCallback as the handler for when one of them does change.

Then it is up to you to get the attribute value into the DOM. If the DOM is simple you can do like I am and just regenerate it each time.

Something more complex will require a more complex algorithm.

Some people will tell you to use LITHtml. I find it to be annoying. Most of my components are very small and don't need the complexity.

Extended

Yes you can pass a callback function as a string. In other words, you pass the name of the callback function as a string and then, in your component, call eval. BUT I would not recommend it. Doing so has to many limits and can be used for evil and nefarious purposes. :)

Instead:

  • Provide a property on the component that you can set to the callback function.
  • Provide a function on the component that you would call with your callback function
  • Dispatch events from the component and let addEventListener handle it for you. Can you provide more details of what you want to do with the function

Here are two helpful questions/answers: * custom web component event call back function in tag * can i pass function as attribute to web component?

UPDATE

The question was asked how to avoid rendering more often than needed when several attributes or properties were set. This is often called "debouncing".

Here is one option:

let renderTimeout = null;
function render(el) {
  if (renderTimeout) {
    clearTimeout(renderTimeout);
  }

  renderTimeout = setTimeout(() => {
    el.shadowRoot.innerHTML = `<div>${el._test}</div>`;
  }, 0);
}

This will set a 0 time timeout, which means that it will happen as soon as it can, often as soon as the current code has finished running. Then the render operation will happen.

Intervalia
  • 10,248
  • 2
  • 30
  • 60
  • can I also pass functions (maybe also as a string) to the component? – Andreas May 10 '19 at 06:38
  • 1
    See my extended response. Can you provide more details of what you want to do with the function. – Intervalia May 10 '19 at 13:41
  • The extended response is absolutely fine for what I want to do. Thank you :) – Andreas May 10 '19 at 13:42
  • Is there a way to prevent `render(this)` being called twice when setting two attributes back-to-back. – sabithpocker Jun 07 '19 at 22:58
  • see my update for one way to "debounce" functionality. – Intervalia Jun 08 '19 at 15:16
  • Which is the lit-html complexity, it's just the minimal optimized built-in you can reach when your're going to update values in DOM elements without re-render every time all the DOM tree... only template literals and render element in a optimized way .... https://stackoverflow.com/questions/70657298/render-lit-lit-html-templateresult-as-string – cicciosgamino Aug 09 '23 at 09:44