Render lit / lit-html TemplateResult as string
Asked Answered
B

2

5

In lit/lit-html/lit-element, a standard component is the TemplateResult (usually HTMLTemplateResult), created like:

function renderMe(msg) {
    return html`<div>Hello ${msg}!</div>`;
}

and of course the power and efficiency of the library is that subsequent calls will reuse the same <div> Element and only replace the changed fragments.

For testing the renderMe() function above, however, it would be helpful to be able to see the return value as a standard string, like:

assert.equal(RENDER_AS_STRING(renderMe('kimiko')), '<div>Hello kimiko!</div>');

and fix any bugs in the function before testing how it renders into the browser itself.

Is there a function like RENDER_AS_STRING either in lit itself or in a testing library? I have searched and not found one.

Basilio answered 10/1, 2022 at 18:31 Comment(0)
J
6

The result of execution contains html strings and values that alternate:

enter image description here

We can combine them in the same order:

function renderMe(msg) {
    return html`<div>Hello ${msg}!</div>`;
}

const getRenderString = (data) => {
  const {strings, values} = data;
  const v = [...values, ''] // + last emtpty part
  return strings.reduce((acc,s, i) => acc + s + v[i], '')
}

console.log(getRenderString(renderMe('SO')))

You can test it in the playground

And the recursive version

import {html, css, LitElement} from 'lit';

function renderMe(msg) {
    return html`<p>Hello ${msg}!</p>`;
}

function renderBlock(msg) {
    return html`<div>${renderMe(msg)}</div>`;
}

const getRenderString = (data) => {
  const {strings, values} = data;
  const v = [...values, ''].map(e => typeof e === 'object' ? getRenderString(e) : e )      
  return strings.reduce((acc,s, i) => acc + s + v[i], '')
}

document.getElementById('output').textContent = getRenderString(renderBlock('SO')) 
Jovitta answered 10/1, 2022 at 20:34 Comment(3)
Wonderful answer. Thank you!Basilio
Note that this does not totally work because the values can themselves contain Templates so need a recursive solution.Basilio
@MichaelScottCuthbert, you can test it now.Jovitta
B
2

@Daniil Loban's answer works great if the arguments are strings, but if they might themselves be TemplateResults or arrays of TemplateResults (which are all allowed by spec), it needs a more complex answer:

export function template_as_string(data) {
    const {strings, values} = data;
    const value_list = [...values, ''];  // + last empty part
    let output = '';
    for (let i = 0; i < strings.length; i++) {
        let v = value_list[i];
        if (v._$litType$ !== undefined) {
            v = template_as_string(v);  // embedded Template
        } else if (v instanceof Array) {
            // array of strings or templates.
            let new_v = '';
            for (const inner_v of [...v]) {
                new_v += template_as_string(inner_v);
            }
            v = new_v;
        }
        output += strings[i] + v;
    }
    return output;
}
Basilio answered 13/1, 2022 at 23:22 Comment(2)
This does not cover where lit allows binding to attributes without surrounding the interpolation in quotes: <img src=${srcValue} />.Oast
right -- at present it also doesn't cover if one of the values is null or undefined (which cannot have properties read of it).Basilio

© 2022 - 2024 — McMap. All rights reserved.