简体   繁体   中英

creating a vanilla js web-component with separate html and js files

While creating vanilla JS web-components, I am trying to figure out a way to keep the template html outside the JS file, preferably in a separate HTML file.

I did look into multiple implementations of native JS web-component implementations notably here , single file and a very nice writeup here .

All of the implementations end up putting the html template inside the JS using string literals or similar methods. My question is, is it even possible to make two separate files, for example my-component.html for template and my-component.js for JS code?

I have seen this approach working nicely in Aurelia framework, it is very clean and keeps the code where it belongs.

I tried using html imports, by creating a html file for template and including js inside it.

<link rel="import" href="my-component.html">

that theoretically should work but html imports is already abandoned by browsers.

My second approach, that seems a bit hack to me, but it does work is to pull the template html from server in the connectedCallback

// my-component.js

class MyComponent extends HTMLElement {

    constructor() {
        super();
    }

    connectedCallback() {
        this.initShadowDom();

    }

    async initShadowDom() {
        let shadowRoot = this.attachShadow({mode: 'open'});
        shadowRoot.innerHTML = await this.template;
    }

    async fetchData() {
        return await fetch('my-template-url')
            .then(function(response) {
                return response.text();
            });
    }

    get template() {

        return (async () => {
            return await this.fetchData();
        })();
    }
}
customElements.define('my-component', MyComponent);

from the server side I simply return pure html

// my-component.html

<style>
@import 'https://fonts.googleapis.com/icon?family=Material+Icons';
@import 'https://code.getmdl.io/1.3.0/material.${this.theme}.min.css';
@import 'http://fonts.googleapis.com/css?family=Roboto:300,400,500,700';
@import 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'
}
table {
    border-collapse: collapse;
}
td {
    text-align: center;
    border: 1px solid #f7f7f7;
    padding: 1rem;
}
th {
    background-color: #f7f7f7;
    text-align: center;
    padding: 1em;
    border: 1px solid #e9e9e9;
}
    </style>
<div class="search-list table-responsive">
<table width="100%" class="table table-bordered table-striped table-hover">
    <thead class="thead-light">
    <tr>
        <th width="1rem">
            <input type="checkbox">
        </th>
        <th>
            ID
        </th>
        <th>
            name
        </th>

    </tr>
    </thead>
    <tbody>
    <tr>
        <td><input type="checkbox"></td>
        <td>
            1
        </td>
        <td>
            some name
        </td>

    </tr>

    </tbody>
    </table>
</div>

Notice how I have skipped the <template> tag altogether in the response coming from server, as obviously if I use <template> tag, I will have to use dom parsing to actually put the html into dom.

This does work but it looks so hackish.

Does anyone has a better approach?

You solution is good but you could cut in the Javascript code:

class MyComponent extends HTMLElement {

    async connectedCallback() {
        let res = await fetch( 'my-component.html' )

        this.attachShadow( { mode: 'open' } )
            .innerHTML = await res.text()
    }

}
customElements.define( 'my-component', MyComponent )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM