Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Seperating HTML and CSS from Javascript Lit Element

We are working with Lit Element and our component files are getting rather large because we have to write styling and html within the .js file. Is there a way to split them in separate files and then import them into our components? What would be the best approach to doing so? HTML and CSS especially are making the workflow difficult for us since our files are getting too bloated.

Update: Supersharp's advice helped me seperate the CSS, and the html template but I am having issue binding the html template with specified properties. I have a service file which is making an XML request for a file i specified, and them I am importing it in my component file. Here is my component:

import { LitElement, html} from '@polymer/lit-element';

import HtmlLoader from './Service/HtmlLoader';

class TestAnimatedButtonElement extends LitElement {

  static get properties() {
    return {
      _text: {
        type: String
      }
    }
  }

  constructor() {
    super();
    this.htmlTemplate = HtmlLoader.prototype.getHtmlTemplate('/src/Components/ExampleElements/test-animated-button-element.html');
  }

  render(){
    this.htmlTemplate.then( template => this.shadowRoot.innerHTML = template)
    return html( [this.htmlTemplate] );
  }

  handleButton(e) {
    //e.preventDefault();
    console.log('Button pressed');
  }
}
customElements.define('test-animated-button-element', TestAnimatedButtonElement);

And this is my html file:

<link rel="stylesheet" href="src/Components/animatedButton/animated-button-element.css"/>
<a href="#" class="btn btn--white btn--animated" @click="${(e) => this.handleButton(e)}">${this._text}</a>

And this is the service file I am using for making XMLHttpRequest:

export default class HtmlLoader {

    xhrCall(url){
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = () => resolve(xhr.responseText);
        xhr.onerror = () => reject(xhr.statusText);
        xhr.send();
      });
    }

    getHtmlTemplate(url) {
       return this.xhrCall(url);
    }

}
like image 485
Filip Skukan Avatar asked Nov 29 '18 14:11

Filip Skukan


People also ask

What is LitElement?

LitElement is a simple base class for creating fast, lightweight web components that work in any web page with any framework. LitElement uses lit-html to render into shadow DOM, and adds API to manage properties and attributes.

Can you separate CSS and HTML?

Either you make proper separation between content (HTML) and style (CSS), Or you mix both, either by putting CSS code within HTML files, or simply by using HTML analogs ( <div color="red">I'm red. </div> ).

Does material UI use CSS in Js?

Material UI is a React component that aims to ensure fast and easy web application development. Material UI is not only restricted to Material UI components, but can also use the CSS-in-JS solution through the @material-ui/styles package here.


1 Answers

This is the downside of ES Modules, and it's why Google pushed for HTML Imports for so long despite other browser vendors being dead against them.

You can seperate out your CSS and JS, but you're adding additional round trips to the browser if you get them at run time. In particular your templates are retrieved asynchronously, if you wait for them you'll be blocking the main JS thread while you do, and if you await them you'll break lit-element as it expects the root template to be synchronous.

You could (but shouldn't) use until:

import { until } from 'lit-html/directives/until';

...

render(){
    return html`until(
        this.loadTemplate(
            '/src/Components/ExampleElements/test-animated-button-element.html',
            this.foo, 
            this.bar), html`Loading...`)`;
}

async loadTemplate(path, ...parts) {
    // You're using templates you can use fetch, instead of XMLHttpRequest
    const r = await fetch(path, { method: 'GET', credentials: 'same-origin' });
    if(r.ok)
         return html(await r.text(), parts);

    return html`Not Found: ${path}`
}

However, I notice that you're using bare modules (i.e. without a . at the start and .js at the end) so you can't be serving this file direct to browsers (none can handle bare modules yet). In short: you must have something that turns:

import { LitElement, html} from '@polymer/lit-element';

Into:

import { LitElement, html } from '../../node_modules/@polymer/lit-element/lit-element.js';

Or bundles those together into a single file. Given you're doing that why not make your bundler (Webpack, RollupJS, Browserify, whatever) import your CSS and HTML into the file at build time?

Finally, with any of these solutions you lose write-time parts<->html relationship, which doesn't matter for CSS but will make the HTML parts much harder to develop/maintain. With a library like lit you want your HTML in your JS, for the same reasons that JSX/React/Preact works so well. If your files are too large then I don't think the solution is to split them by JS/CSS/HTML - instead split them into more components.

So, for instance, suppose you have a massive <product-detail> control...

Don't

  • product-detail.js
  • product-detail.html
  • product-detail.css

Do

  • product-detail.js
  • product-detail-dimensions.js
  • product-detail-specs.js
  • product-detail-parts.js
  • product-detail-preview.js
  • etc

Break each component down to do a specific task. The whole point of lit-element is to make all these as simple as possible to create, and you want lots of small, self-contained components.

like image 131
Keith Avatar answered Sep 21 '22 05:09

Keith