In vue3 I have this:
<template>
<ChildComponent>
<div>
<input type="text" v-model="myValue"/>
</div>
</ChildComponent>
</template>
Here, in the child component the slot is rendered with input field, and it is binded to the myValue from the parent component.
How could I make it working with render function instead od declarative template like above?
In my app the slot inner html comes from the api rest service, but inserting it to the slot via v-html obviously does not work.
You can compile your inner html to a render function.
Here's a trick: we should provide a render context to the compiled render function with myValue
. The problem that we cannot provide myValue
as it is since the compiled function looks like that:
(function anonymous(Vue
) {
const _Vue = Vue
const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = ["onUpdate:modelValue"]
return function render(_ctx, _cache) {
with (_ctx) {
const { vModelText: _vModelText, createElementVNode: _createElementVNode, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", null, [
_withDirectives(_createElementVNode("input", {
type: "text",
"onUpdate:modelValue": $event => ((myValue) = $event)
}, null, 8 /* PROPS */, _hoisted_1), [
[_vModelText, myValue]
])
]))
}
}
})
As you see the problem in
"onUpdate:modelValue": $event => ((myValue) = $event)
So basically the ref is overwritten, not changed (myValue.value = $event
).
So we need to wrap our context into a reactive, that way when myValue
in the context is changed, our linked myValue
ref is changed too:
<script setup>
import ChildComponent from './ChildComponent.vue';
import {compile, ref, reactive} from 'vue';
const myValue = ref('test');
const compiled = compile(`<div>
<input type="text" v-model="myValue"/>
</div>`);
const content = () => compiled(reactive({myValue}));
</script>
<template>
<div>{{ myValue }}</div>
<ChildComponent>
<content/>
</ChildComponent>
</template>
To support the compile you should change Vue's runtime:
vite.config.js:
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
vue: 'vue/dist/vue.esm-bundler.js',
}
}
})