Florestan Korp
Florestan Korp

Reputation: 712

Rendering HTML5 template in web-component slot from different file

I'm just getting started with web-components and trying to abstract repeating HTML code to their own files, so not like I'm doing with <nav-bar> and <bootstrap-wrapper>, but more of a component based approach.

Now, I want to structure my project in such a way that my template is sent to a slot in index.html to render.

How I can render welcome.html inside of my index.html, also, how do I then navigate from welcome.html to another template

index.html

<!DOCTYPE html>
<html lang="en">

<body>
    <bootstrap-wrapper>
        <nav-bar></nav-bar>
        <span slot="content"></span>
    </bootstrap-wrapper>
</body>

<script src="actio.js"></script>

</html>

actio.js

customElements.define(
  'nav-bar',
  class NavBar extends HTMLElement {
    connectedCallback() {
      this.innerHTML = `
        <nav class="nav">
          <a class="nav-link" href="welcome.html">Home</a>
          <a class="nav-link" href="enter-names.html">Enter Names</a>
          <a class="nav-link" href="calculator.html">Calculator</a>
          <a class="nav-link" href="history.html">History</a>
        </nav>
      `;
    }
  }
);

const template = document.createElement('template');
template.innerHTML = `
    <div class="container">
      <div class="row">
        <div class="col-sm-12 col-md-8 col-lg-6">
          <div class="jumbotron bg-dark text-white">
            <p><slot name="content" /></p>
          </div>
        </div>
      </div>
    </div>
`;

customElements.define(
  'bootstrap-wrapper',
  class BootstrapWrapper extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({
        mode: 'open'
      }).appendChild(
        template.content.cloneNode(true)
      );
    }
  }
);

welcome.html

<template class="welcome">
    <h1>Household Budget Calculator</h1>
    <h3>No more arguments about how to divide your household expenses!</h3>
    <h4>How it works:</h4>
    <ol>
        <li>Enter names</li>
        <li>Fill household expenses</li>
        <li>Each of you fills in their income</li>
        <li>Hit Calculate</li>
        <li>Enjoy a blissful partnership!</li>
    </ol>

    <button onclick="location.href='enter-names.html'"
            type="button"
            class="btn btn-primary">Enter Names</button>
</template>

Upvotes: 2

Views: 1134

Answers (2)

Florestan Korp
Florestan Korp

Reputation: 712

Using fetch() I implemented a method called createComponent() that takes a path and fetches an HTML file, which contains a <template> and appends it to #app-root in my index.html

main.js

function createComponent(path) { // path is relative to root of project

  fetch(path)
    .then(function (response) {
      return response.text();
    })
    .then(function (html) {
      const doc = new DOMParser().parseFromString(html, 'text/html');
      const template = doc.querySelector('head > template');

      document.querySelector('#app-root').innerHTML = '';
      document.querySelector('#app-root').appendChild(template.content);
    })
    .catch(function (err) {
      console.error('Something went wrong.', err);
    });
}

index.html

<!DOCTYPE html>
<html lang="en">

<body>
    <span id="app-root">
        <!-- APP ROOT -->
    </span>
</body>
<script src="main.js"></script>

</html>

template.html

<template>
    <div class="welcome">
        <h2>Welcome to the Household Budget Calculator</h2>
        <p>We help you divide household expenses fairly.</p>
        <h4>How it works:</h4>
        <ol>
            <li>Enter names</li>
            <li>Fill household expenses</li>
            <li>Each of you fills in their income</li>
            <li>Hit Calculate</li>
            <li>Enjoy a blissful partnership!</li>
        </ol>
        <div class="button-area">
            <button onclick="next()">Next</button>
        </div>
    </div>
</template>

Upvotes: 0

Supersharp
Supersharp

Reputation: 31181

Use fetch(), which is an asynchronous function.

In your BoostrapWrapper class, add a method:

async function loadTemplate( filename ) {
    var response = await fetch( filename )
    var text = await response.text()
    this.querySelector( span[slot=content]' ).innerHTML = text
}

No need to include the code in a <template> element unless you use Javascript code inside. In this latter case you'll need to create a temporary <template> element.

You can then call the method with any HTML file.

Upvotes: 2

Related Questions