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'
function render() {
return [
'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) => {
const 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])
}
function render() {
return splitByArray(input, Object.keys(replacements).map((part) => {
return replacement[part]?.() ?? part
}))
}
</script>
<template>
<component :is="render" />
</template>