When my application loads, it will call the backend to obtain a description of components the user decides to add.
These objects should have a function to generate their html, take a button for example: it will export an html() method that returns a string containing an HTML text:
const buttonText = "I'm a button"
export default {
html() {
return `<button>${buttonText}</button>`
}
}
On the frontend side, I will be using import statement to load the aforementioned button module and store it in a JSON object, sort of like a plugins manager.
When it comes time to render it, I tried using Dynamic:
<For each={plugins()} fallback={<p>Loading...</p>}>{ plugin =>
<div>
<Dynamic component={plugin.module.html()}>
</Dynamic>
<div>
}</For>
But it falls with DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('<button>I'm a button</button>') is not a valid name.
Which makes sense since it is expecting something like a "div"
element, not a string like <button>I'm a button</button>
.
What would be the correct way to render HTML strings in SolidJS?
A simple solution would be just to use innerHTML
like so:
<For each={plugins()} fallback={<p>Loading...</p>}>
{ plugin =>
<div innerHTML={plugin.module.html()}>
<div>
}</For>
If you need something more interactive, perhaps using template
and cloneNode
which is what Solidjs compiles your code to normally.
Here is a full example below. I created it on the playground.
import { render, template } from "solid-js/web";
import { For } from "solid-js";
function MakeButton() {
const plugins = ["<button>Click me</button>"];
const handleClick = () => {
console.log("Clicked button");
}
const createElem = (el: string) => {
const elem = template(el, 2);
const ret = elem.cloneNode(true);
if (ret.tagName === "BUTTON") {
ret.onclick = handleClick;
}
return ret;
}
return (<>
<For each={plugins} fallback={<div>Loading...</div>}>
{plugin =>
<div>{createElem(plugin)}</div>
}
</For>
</>);
}
render(() => <MakeButton />, document.getElementById("app"));