Render lit / lit-html TemplateResult as string

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.

Upvotes: 6

Views: 5128

Answers (2)

updated/expanded for a broader mix of TemplateResults, arrays and primitives...

export function template_as_string(data) {
    // template
    const {strings = null, values = []} = data ?? {};
    // if not a template
    if(!strings) return data ?? '';
    return strings.reduce((res, txt, i)=>{
        const v = values[i] ?? '';
        res.push(txt);
        if(Array.isArray(v)){
            res.push(...(v.map(template_as_string)))
        }else{
            res.push( template_as_string(v) );
        }
        return res;
    }, []).join('');
}


export function test_template_as_string({html=()=>{}, src='./image.webp', make=template_as_string}, ...tests){
return [
html`<img src=${src} />`,
html`<b>${ [1,2,html`<b>bb${7}b</b>`] }</b>`,
...tests
].map( stuff=> make(stuff) )
}
/*
// str info at https://lit.dev/docs/localization/overview/#strings-with-expressions
console.log(test_template_as_string({html}, html`<b>${ str`${ 123 }` }</b>`));
console.log(test_template_as_string({html}));
*/

Upvotes: 2

Daniil Loban
Daniil Loban

Reputation: 4381

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')) 

Upvotes: 6

Related Questions