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.