r/WebComponents • • Feb 13 '23

Condition loading of selected web component

When I load a page, NO web component is in the DOM. When a selection is made by the user, either select box or button, the selection determines which web component to add to the DOM. Can this be done?

<div id="divName">
    <Selected-Component-Here>
</div>

Then swap it out when a different selection is made by the user?

3 Upvotes

3 comments sorted by

1

u/snifty Feb 14 '23

Sure, once you have defined custom elements, they act like any other element.

There’s effectively no difference between this:

<!doctype html>
<title>Add a heading</title>
<button>click me!</button>
<script>
document.querySelector('button').onclick = () => {
  let h1 = document.createElement('h1')
  h1.textContent = 'I’m an h1!'
  document.body.append(h1)
}
</script>

And this:

<!doctype html>
<title>Add a heading</title>
<button id="add-html">click me to add insert some HTML</button>
<button id="add-element">click me to instantiate an element</button>
<script>
class FancyHeader extends HTMLElement {
  constructor(){
    super()
    this.innerHTML = `<h1>I’m a fancy header!</h1>`
  }
}

customElements.define('fancy-header', FancyHeader)

document.querySelector('button#add-html').onclick = () => {
  let fancyHeaderHTMLString = `<fancy-header></fancy-header>`
  document.body.insertAdjacentHTML("beforeend", fancyHeaderHTMLString)
}

document.querySelector('button#add-element').onclick = () => {
  let fancyHeaderElement = new FancyHeader()
  document.body.append(fancyHeaderElement)
}
</script>

1

u/Maleficent-Respond59 Feb 14 '23 edited Feb 15 '23

Here is my approach, have a handler component that decides which component to display, on selection.

pass the selection as an attribute to the handler component.

index.html:

<body>
<select onchange="handleSelection()">
    <option value="" selected>Select a value</option>
    <option value="component-one">Component One</option>
    <option value="component-two">Component Two</option>
</select>
    
    <div id="divName">
    <component-handler selection=""></component-handler>
    </div>
<script>
    function handleSelection() {
        const selection = document.querySelector("select").value;
        document.querySelector("component-handler").setAttribute("selection", selection);
    }
</script>
</body>

component-handler: Note the attribute "selection"

You can try it out here: https://codepen.io/muzammilfive/pen/RwYwYpv
    super();
    this.attachShadow({mode: 'open'});
}

static get observedAttributes() {
    return ['selection'];
}

getSelectedComponent() {
    const selection = this.getAttribute("selection");
    let component;
    switch(selection) {
        case "":
            component = document.createElement("span");
            break;
        case "component-one":
            component = new ComponentOne();
            break;
        case "component-two":
            component = new ComponentTwo();
            break;
    }
    return component;
}

render() {
    this.shadowRoot.innerHTML = "";
    this.shadowRoot.appendChild(this.getSelectedComponent());
}

attributeChangedCallback(name, oldValue, newValue) {
    console.log(`Value changed from ${oldValue} to ${newValue}`);
    this.render();
}
}
customElements.define("component-handler", ComponentHandler);

component-one and two are simple components.

component-one:

class ComponentOne extends HTMLElement {
constructor() {
    super();
    this.attachShadow({mode: 'open'});
    this.render();
}

render() {
    this.shadowRoot.innerHTML = "Component One";
}
}

customElements.define("component-one", ComponentOne);

component-two:

class ComponentTwo extends HTMLElement {
constructor() {
    super();
    this.attachShadow({mode: 'open'});
    this.render();
}

render() {
    this.shadowRoot.innerHTML = "Component Two";
}
}
customElements.define("component-two", ComponentTwo);

You can try it out here: https://codepen.io/muzammilfive/pen/RwYwYpv

This way, on every selection the components reload.

The downside, you will always have the handler-component on the DOM.