Replacing parts of a string with Vue components
Someone in the Inertia.js Discord server recently asked for a way to replace parts of an input string with custom Vue components:
I had never done that, but thought it would be fun to find out how to do it. Turns out, you can't simply replace your placeholder with some HTML that calls a Vue component:
const input = 'This is some text with a __placeholder__'const html = input.replace('__placeholder__', '<custom-component></custom-component>')
So I looked into it deeper, and found out that the correct way to do this is to use a render function. For the example above, the corresponding render function would be:
import { h } from 'vue'import Placeholder from './placeholder.vue'const render = () => [ 'This is some text with a ', h(Placeholder)]
This is pretty simple, but we need to find a way to split the input string by multiple separators, and replace these separators with the render function. Since I'm lazy, I asked ChatGPT to do it for me:
Apparently, ChatGPT perfectly understood my gibberish prompt and gave me a fully working function. After cleaning it up a bit, the rest is a matter of mapping the result of this function to the components we want to replace:
<script setup>import Foo from './Foo.vue'import Bar from './Bar.vue'const input = 'This __foo__ and this __bar__ are Vue components'const replacements = { '__foo__': () => h(Foo), '__bar__': () => h(Bar),}function splitByArray(inputString, splitArray) { return splitArray.reduce((result, splitString) => { return result.reduce((newResult, str) => { let parts = str.split(splitString); for (let i = 0; i < parts.length - 1; i++) { newResult.push(parts[i], splitString); } newResult.push(parts[parts.length - 1]); return newResult; }, []); }, [inputString]);}const render = () => splitByArray(input, Object.keys(replacements).map(part) => { return replacement[part]?.() ?? part})</script><template> <component :is="render" /></template>