Hyperhtml: Import html as an ES module

Created on 26 Apr 2018  路  13Comments  路  Source: WebReflection/hyperHTML

Simple question, something I haven't been able to figure out how to do, and can't find any other related issues or answers. Is there a way to import html as an ES module and use it with hyperhtml?

I'm using hyperhtml with native Custom Elements, trying to do something like this:

import hyperhtml from 'hyperhtml';
import tpl from './my-element.tpl.html';

class myElement extends HTMLElement {
    // Custom Element constructor.
    constructor() {
        super();
        this.render();
    }

    render() {
        return hyperhtml.bind(this)`${tpl}`;
    }
}

I've tried a few different ways, can't find anything that works...

I appreciate your help. Pretty new with hyperhtml, but great project! Love it so far.

invalid question

Most helpful comment

@thezimmee just adding my two cents here: I built https://github.com/joshgillies/hyperify which is a browserify transform based off the work @WebReflection mentioned here https://github.com/WebReflection/viperHTML#handy-patterns. Where initially I felt like a tooling approach abstracting the HTML template (and interpolations) from code was of benefit, but reality is you'll spend more time wrestling with a build pipeline to cater for all scenarios you'll encounter in an app than you would just authoring the template literal as is. So in summary and to borrow a phrase from @WebReflection, YAGNI. 馃槃

All 13 comments

i think you can do
import hyperHTML from 'hyperhtml/esm';
see the bottom of the git page: https://github.com/WebReflection/hyperHTML

Thanks for your response, but that's not exactly what I'm asking (import hyperhtml from 'hyperhtml' works fine to import hyperhtml as an ES module)...

It's the tpl part i'm trying to figure out... how to import an html file as a module and use it with hyperhtml:

import tpl from './sidebars.tpl.html';
...
return hyperhtml.bind(this)`${tpl}`;

The above does not work because tpl is a normal string when it's imported. Any ideas on how to use an imported HTMl file with hyperhtml? Is this possible?

not by itself, i did a rollup plugin to load them by appending an export default, making them modules, i can share that with you if needed

Sure, I'd appreciate that.

Plugin

const { createFilter } = require('rollup-pluginutils');

module.exports = function string(opts = {}) {
    if (!opts.include) {
        throw Error('include option should be specified');
    }

    const filter = createFilter(opts.include, opts.exclude);

    return {
        name: 'string',

        transform(code, id) {
            if (filter(id)) {
                return {
                    code: 'export default (render, model, hyper) => render`' + code + '`;',
                    map: { mappings: '' }
                };
            }
        }
    };
}

use it in plugins

hypertemplate({
            include: '**/*.tpl.html'
        }),

I do appreciate @pinguxx help here, but I'm not sure I understand where this is going.

To start with, _hyperHTML_ is not ah handlebar like alternative, or any alternative to external templates as strings.

The whole strength of _hyperHTML_ is the ES2015 Template Literals tag feature, if you don't have that in place, there's quite possibly no gain in using _hyperHTML_ because all interpolations won't have access to the surrounding scope and/or context.

What @pinguxx does makes sense only if templates uses those helpers, but it's not clear from this ticket how you are planning to use your external files or what's inside those.

Do you mind showing an example of what you are trying to do? That would help me understanding, thanks.

@WebReflection To summarize, I need to be able to import a string from an html file and convert it to a template literal so it can be used with hyperhtml. The reason for this is I would like to put my HTML into separate files and import them for use with hyperhtml in a custom element to gain all the benefits you described above. I do not believe this is an invalid question or scenario. I will try to be more clear with my example above:

// my-element.tpl.html
<p>Testing, testing, 123</p>

// my-element.js
...
import tpl from './my-element.tpl.html';

class myElement extends HTMLElement {
    ...
    render() {
        return hyperhtml.bind(this)`${tpl}`;
    }
}

I am trying to render tpl, an imported html file. This doesn't work, and I believe the challenge is converting a normal string (which is what the html file is imported as) to a template literal (which as you described above is what hyperhtml needs to use it). So my question is how to import an html file and use it with hyperhtml? @pinguxx has provided one potential solution -- which tries to convert the html file string to a template literal via a rollup plugin -- though I haven't been able to get that rollup plugin working.

I hope this is more clear as to my original intent. If not I can upload an example to github...

@thezimmee if you want to explicitly inject HTML you can use either an array or an intent.

array

import tpl from './my-element.tpl.html';
class myElement extends HTMLElement {
    render() {
        return hyperhtml.bind(this)`${[tpl]}`;
    }
}

intent

import tpl from './my-element.tpl.html';
class myElement extends HTMLElement {
    render() {
        return hyperhtml.bind(this)`${{html: tpl}}`;
    }
}

However, that render will replace the whole content per each render() call, because it basically simply inject HTML through innerHTML, which is probably not what you want.

alternative

If you don't need interpolations, you can use template tags procedurally:

import tpl from './my-element.tpl.html';
class myElement extends HTMLElement {
    render() {
        return hyperhtml.bind(this)([tpl]);
    }
}

All hyperHTML produces are callbacks, nothing really too special. As long as you have an Array as first argument, and eventually interpolated values as second, third, and X arguments, it works.

Does this help your case anyhow?

@WebReflection Yes, this definitely helps, thank you so much. The alternative approach works as you described without interpolation. It would be awesome to get this working with interpolation in a seamless / programmatic way... perhaps somehow mapping an object to the template literal variables. If you have any thoughts or ideas that may point me in the right direction (I don't know much about template literals), that would be awesome. If I figure something out to that end I will post here.

@thezimmee you have two options there:

  • evaluate your text
  • use a pre-processor, as mentioned by @pinguxx

The evaluation part is like the following one:

function tagArguments(html, model) {
  var interpolations = [];
  html.replace(/\$\{([^}]*)\}/g, function ($0, $1) {
    interpolations.push(Function('model', 'return ' + $1)(model));
  });
  return [html.split(/\$\{[^}]*\}/)].concat(interpolations);
}

hyperHTML.bind(document.body).apply(
  null,
  tagArguments('<p>${model.name}</p>', {name: 'hyperHTML'})
);

The pre-processor one is the one described in viperHTML SSR module:
https://github.com/WebReflection/viperHTML#handy-patterns

You'll end up without any need for evaluation, and code as such:

module.exports = (render, model) => render`
<p>${model.name}</p>
`;

At that point what you'd do is:

const named = require('./template-named.js');

named(hyperHTML.bind(document.body), {name: 'Hello there'});

And everything should work.

Closing this since there's no action to take from my side.

Hope these hints will help.

@thezimmee just adding my two cents here: I built https://github.com/joshgillies/hyperify which is a browserify transform based off the work @WebReflection mentioned here https://github.com/WebReflection/viperHTML#handy-patterns. Where initially I felt like a tooling approach abstracting the HTML template (and interpolations) from code was of benefit, but reality is you'll spend more time wrestling with a build pipeline to cater for all scenarios you'll encounter in an app than you would just authoring the template literal as is. So in summary and to borrow a phrase from @WebReflection, YAGNI. 馃槃

@joshgillies agreed on the conclusion, yet that transformer is pretty sweet as solution !!!

Many thanks to everybody... @WebReflection and @pinguxx for giving very good direction to see if abstracting the HTML is the right approach for me; and great thoughts as well, @joshgillies, on the caution that it may be more heartache than it's worth. You all have been very helpful! Much appreciated!

_Update_:

For the record, the solution I am using is a slightly modified version of @pinguxx's rollup plugin (since I was already using rollupjs). At first I couldn't get it working, but with @WebReflection's guidance and context with the "pre-processor" approach), it is now working amazingly, at least so far. If that changes and it becomes more of a pain to maintain than it's worth -- as @joshgillies concluded -- I will update this post with my experience. Thanks again guys!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

liming picture liming  路  7Comments

BentleyDavis picture BentleyDavis  路  8Comments

ansarizafar picture ansarizafar  路  8Comments

WebReflection picture WebReflection  路  3Comments

bschlenk picture bschlenk  路  3Comments