Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web components: dynamic content inside <template>

I am using the following code to create instances of a Web component:

<main></main>
<template id="my-template">
  <style>
    p { color: green; }
  </style>
  <p>I'm in Shadow DOM.</p>
</template>


<script>
  let tmpl = document.querySelector('#my-template');
  class AppDrawer extends HTMLElement {

    constructor() {
      super(); 
      this.root = this.attachShadow({mode: 'open'});
      //this.root.appendChild(tmpl.content.cloneNode(true));
    }

    set details(user) {
      this.root.innerHTML = `<h2> ${user.company.name}</h2>`
    }
  }
  window.customElements.define('app-drawer', AppDrawer);


  async function asyncCall() {
    const res = await fetch("https://jsonplaceholder.typicode.com/users");
    const json = await res.json();

    const main = document.querySelector("main");
    json.forEach(user=> {
      const el = document.createElement("app-drawer");
      el.details = user;
      main.appendChild(el);
    });
  }

  asyncCall();

</script>

By running the above, I am getting some names as output. So far so good. Now, trying to use the <template> instead by deleting the comment inside the constructor and deleting el.details = user; as well, I am getting multiple I'm in Shadow DOM. My question is how can I achieve the dynamic content (different user names) of the first case by using the second approach?

like image 235
Unknown developer Avatar asked Mar 20 '26 02:03

Unknown developer


2 Answers

You just need to replace your el.details = user with a statement that adds the appropriate HTML to the shadow DOM. (This makes the setter irrelevant, so I have commented it out.)


EDIT:
As requested in a comment, I've updated the snippet to pass user.company.name into the web component as an attribute during its construction, rather than setting the value imperatively afterward.

(Note that I removed the <p> element from the template in favor of simply including the <h2> element where the userCompanyName will be displayed.)

let tmpl = document.querySelector('#my-template');
class AppDrawer extends HTMLElement {
  constructor() {
    super();
    this.root = this.attachShadow({ mode: 'open' });
 
    // Creates a copy of the `<template>` element's contents
    const copy = tmpl.content.cloneNode(true);

    // Selects a target element withinin the copy
    const header = copy.querySelector("H2");

    // Copies the component instance's `usernamecomp` attribute to its `userCompName` property
    this.userCompName = this.getAttribute("usercompname");

    // Sets the target element's contents equal to the`userCompName` property
    header.innerHTML = this.userCompName;

    // Appends the augmented copy to the shadowDOM
    this.root.appendChild(copy);
  }
}
window.customElements.define('app-drawer', AppDrawer);

async function asyncCall() {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  const json = await res.json();
  const main = document.querySelector("main");

  json.forEach(user => {
 
    // Identifies the string to pass into the component instance as an attribute
    const userCompName = user.company.name;
 
    // Writes HTML to be used to instantiate an `<app-drawer>` element with a `usercompname` attribute
    const drawerInstance = `<app-drawer usercompname=${userCompName}></app-drawer>`;
 
    // Creates the new `<app-drawer>` element, appended to the `<main>` element     
    main.insertAdjacentHTML("beforeend", drawerInstance);
  });
}

asyncCall();
<main></main>
<template id="my-template">
  <style>
    h2 { color: green; }
  </style>
  
  <h2>I'm in Shadow DOM.</h2>

</template>
like image 57
Cat Avatar answered Mar 22 '26 14:03

Cat


You can pass the loaded value as a parameter to the custom element create with new:

json.forEach( user => main.appendChild( new AppDrawer( user ) ) )

You just need to define the custom element constructor() with an argument:

constructor( user ) {
    ...
}

See a complete example below:

class AppDrawer extends HTMLElement {
    constructor( user ) {
      super()
      this.attachShadow( {mode: 'open'} )
          .innerHTML = `<style> p { color: green } </style>
              <p> ${user.company.name} </p>`
    }
}
window.customElements.define( 'app-drawer', AppDrawer )

async function asyncCall() {
    const res = await fetch( "https://jsonplaceholder.typicode.com/users" )
    const json = await res.json()

    const main = document.querySelector( "main" )
    json.forEach( user => main.appendChild( new AppDrawer( user ) ) )
}

asyncCall()
<main></main>
like image 21
Supersharp Avatar answered Mar 22 '26 15:03

Supersharp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!