title: Web component html template import form separated file
body: <p>New to web components and web techno in general, I am following this interesting tutorial <a href="https://ayushgp.github.io/html-web-components-using-vanilla-js-part-2/" rel="nofollow noreferrer">https://ayushgp.github.io/html-web-components-using-vanilla-js-part-2/</a>.
  The tutorial uses import directives to import the templates as separate files. This way of importing is not supported anymore. I am trying to fix that using the author explanation provided in part 1 (update) in his tutorial, but it does not work.</p>
  <p>Based on the git repo provided with the tutorial <a href="https://github.com/ayushgp/web-components-tutorial" rel="nofollow noreferrer">https://github.com/ayushgp/web-components-tutorial</a>, here are my changes:</p>
  <p>./index.html</p>
  <pre><code>&lt;html&gt;
  
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;title&gt;Web Component Part 2&lt;/title&gt;
  &lt;/head&gt;
  
  &lt;body&gt;
    &lt;people-controller&gt;&lt;/people-controller&gt;
    &lt;script src=&quot;./components/PeopleController/PeopleController.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
  
  &lt;/html&gt;
  </code></pre>
  <p>./components/PeopleController/PeopleController.html</p>
  <pre><code>&lt;template id=&quot;people-controller-template&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;/components/PeopleController/PeopleController.css&quot;&gt;
    &lt;people-list id=&quot;people-list&quot;&gt;&lt;/people-list&gt;
    &lt;person-detail id=&quot;person-detail&quot;&gt;&lt;/person-detail&gt;
    &lt;script src=&quot;./components/PeopleController/PeopleList/PeopleList.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/components/PeopleController/PersonDetail/PersonDetail.js&quot;&gt;&lt;/script&gt;
  &lt;/template&gt;
  </code></pre>
  <p>./components/PeopleController/PeopleController.js</p>
  <pre><code>(async () =&gt; {
    const res = await fetch('/components/PeopleController/PeopleController.html');
    const textTemplate = await res.text();
  
    // Parse and select the template tag here instead
    // of adding it using innerHTML to avoid repeated parsing
    // and searching whenever a new instance of the component is added.
    const HTMLTemplate = new DOMParser().parseFromString(textTemplate, 'text/html')
      .querySelector('template');
  
    function _fetchAndPopulateData(self) {
      let peopleList = self.shadowRoot.querySelector('#people-list');
      fetch(`https://jsonplaceholder.typicode.com/users`)
        .then((response) =&gt; response.text())
        .then((responseText) =&gt; {
          const list = JSON.parse(responseText);
          self.peopleList = list;
          peopleList.list = list;
  
          _attachEventListener(self);
        })
        .catch((error) =&gt; {
          console.error(error);
        });
    }
    function _attachEventListener(self) {
      let personDetail = self.shadowRoot.querySelector('#person-detail');
  
      //Initialize with person with id 1:
      personDetail.updatePersonDetails(self.peopleList[0]);
  
      self.shadowRoot.addEventListener('PersonClicked', (e) =&gt; {
        // e contains the id of person that was clicked.
        // We'll find him using this id in the self.people list:
        self.peopleList.forEach(person =&gt; {
          if (person.id == e.detail.personId) {
            // Update the personDetail component to reflect the click
            personDetail.updatePersonDetails(person);
          }
        })
      })
    }
  
    class PeopleController extends HTMLElement {
      constructor() {
        super();
        this.peopleList = [];
      }
  
      connectedCallback() {
        const shadowRoot = this.attachShadow({ mode: 'open' });
        const instance = HTMLTemplate.content.cloneNode(true);
        shadowRoot.appendChild(instance);
  
        _fetchAndPopulateData(this);
      }
    }
  
    customElements.define('people-controller', PeopleController);
  })()
  </code></pre>
  <p>./components/PeopleController/PeopleList/PeopleList.html</p>
  <pre><code>&lt;template id=&quot;people-list-template&quot;&gt;
    &lt;style&gt;
      .people-list__container {
        border: 1px solid black;
      }
  
      .people-list__list {
        list-style: none
      }
  
      .people-list__name {
        cursor: pointer;
      }
  
      .people-list__list&gt;li {
        font-size: 20px;
        font-family: Helvetica;
        color: #000000;
        text-decoration: none;
      }
    &lt;/style&gt;
    &lt;div class=&quot;people-list__container&quot;&gt;
      &lt;ul class=&quot;people-list__list&quot;&gt;&lt;/ul&gt;
    &lt;/div&gt;
  &lt;/template&gt;
  </code></pre>
  <p>./components/PeopleController/PeopleList/PeopleList.js</p>
  <pre><code>(async () =&gt; {
    const res = await fetch('/components/PeopleController/PeopleList/PeopleList.html');
    const textTemplate = await res.text();
  
    // Parse and select the template tag here instead
    // of adding it using innerHTML to avoid repeated parsing
    // and searching whenever a new instance of the component is added.
    const HTMLTemplate = new DOMParser().parseFromString(textTemplate, 'text/html')
      .querySelector('template');
  
    function _createPersonListElement(self, person) {
      let li = currentDocument.createElement('LI');
      li.innerHTML = person.name;
      li.className = 'people-list__name'
      li.onclick = () =&gt; {
        let event = new CustomEvent(&quot;PersonClicked&quot;, {
          detail: {
            personId: person.id
          },
          bubbles: true
        });
        self.dispatchEvent(event);
      }
      return li;
    }
  
    class PeopleList extends HTMLElement {
      constructor() {
        // If you define a constructor, always call super() first as it is required by the CE spec.
        super();
  
        // A private property that we'll use to keep track of list
        let _list = [];
  
        // Use defineProperty to define a prop on this object, ie, the component.
        // Whenever list is set, call render. This way when the parent component sets some data
        // on the child object, we can automatically update the child.
        Object.defineProperty(this, 'list', {
          get: () =&gt; _list,
          set: (list) =&gt; {
            _list = list;
            this.render();
          }
        });
      }
  
      connectedCallback() {
        // Create a Shadow DOM using our template
        const shadowRoot = this.attachShadow({ mode: 'open' });
        const instance = HTMLTemplate.content.cloneNode(true);
        shadowRoot.appendChild(instance);
      }
  
      render() {
        let ulElement = this.shadowRoot.querySelector('.people-list__list');
        ulElement.innerHTML = '';
  
        this.list.forEach(person =&gt; {
          let li = _createPersonListElement(this, person);
          ulElement.appendChild(li);
        });
      }
    }
  
    customElements.define('people-list', PeopleList);
  })();
  </code></pre>
  <p>./components/PeopleController/PersonDetail/PersonDetail.html</p>
  <pre><code>&lt;template id=&quot;person-detail-template&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;/components/PeopleController/PersonDetail/PersonDetail.css&quot;&gt;
    &lt;div class=&quot;card__user-card-container&quot;&gt;
      &lt;h2 class=&quot;card__name&quot;&gt;
        &lt;span class=&quot;card__full-name&quot;&gt;&lt;/span&gt; (
        &lt;span class=&quot;card__user-name&quot;&gt;&lt;/span&gt;)
      &lt;/h2&gt;
      &lt;p&gt;Website: &lt;a class=&quot;card__website&quot;&gt;&lt;/a&gt;&lt;/p&gt;
      &lt;div class=&quot;card__hidden-content&quot;&gt;
        &lt;p class=&quot;card__address&quot;&gt;&lt;/p&gt;
      &lt;/div&gt;
      &lt;button class=&quot;card__details-btn&quot;&gt;More Details&lt;/button&gt;
    &lt;/div&gt;
  &lt;/template&gt;
  </code></pre>
  <p>./components/PeopleController/PersonDetail/PersonDetail.js</p>
  <pre><code>(async () =&gt; {
    const res = await fetch('/components/PeopleController/PersonDetail/PersonDetail.html');
    const textTemplate = await res.text();
  
    // Parse and select the template tag here instead
    // of adding it using innerHTML to avoid repeated parsing
    // and searching whenever a new instance of the component is added.
    const HTMLTemplate = new DOMParser().parseFromString(textTemplate, 'text/html')
      .querySelector('template');
  
    class PersonDetail extends HTMLElement {
      constructor() {
        // If you define a constructor, always call super() first as it is required by the CE spec.
        super();
  
        // Setup a click listener on &lt;user-card&gt;
        this.addEventListener('click', e =&gt; {
          this.toggleCard();
        });
      }
  
      // Called when element is inserted in DOM
      connectedCallback() {
        const shadowRoot = this.attachShadow({ mode: 'open' });
        const instance = HTMLTemplate.content.cloneNode(true);
        shadowRoot.appendChild(instance);
      }
  
      // Creating an API function so that other components can use this to populate this component
      updatePersonDetails(userData) {
        this.render(userData);
      }
  
      // Function to populate the card(Can be made private)
      render(userData) {
        this.shadowRoot.querySelector('.card__full-name').innerHTML = userData.name;
        this.shadowRoot.querySelector('.card__user-name').innerHTML = userData.username;
        this.shadowRoot.querySelector('.card__website').innerHTML = userData.website;
        this.shadowRoot.querySelector('.card__address').innerHTML = `&lt;h4&gt;Address&lt;/h4&gt;
        ${userData.address.suite}, &lt;br /&gt;
        ${userData.address.street},&lt;br /&gt;
        ${userData.address.city},&lt;br /&gt;
        Zipcode: ${userData.address.zipcode}`
      }
  
      toggleCard() {
        let elem = this.shadowRoot.querySelector('.card__hidden-content');
        let btn = this.shadowRoot.querySelector('.card__details-btn');
        btn.innerHTML = elem.style.display == 'none' ? 'Less Details' : 'More Details';
        elem.style.display = elem.style.display == 'none' ? 'block' : 'none';
      }
    }
  
    customElements.define('person-detail', PersonDetail);
  })();
  </code></pre>
  <p>and the error I get with chrome</p>
  <pre><code>PeopleController.js:23 ReferenceError: currentDocument is not defined
      at _createPersonListElement (PeopleList.js:12)
      at PeopleList.js:59
      at Array.forEach (&lt;anonymous&gt;)
      at HTMLElement.render (PeopleList.js:58)
      at HTMLElement.set (PeopleList.js:42)
      at PeopleController.js:18
  </code></pre>
  <p>How can I fix that?</p>
  <p>Is there a better way than the one provided by the author to import html templates as separated files?</p>
  <p>Thanks!</p>