Reputation: 399
I want to send a 'template' prop to a component then render it. If I send a plain HTML it works, but if I send a Vuetify tag like <v-btn>test</v-btn>
the template does not get compiled.
I know i shouldn't pass a template via props, but this is a specific case: The parent component works as a "template builder" and the child components works as the "result viewer", so I have to pass the created template to the child so that it can compile and show it.
Here's what I've been trying:
main.js
import Vue from 'vue'
import App from './App.vue'
// Some imports here ...
import vuetify from './plugins/vuetify';
new Vue({
vuetify,
render: h => h(App)
}).$mount('#app')
App.vue
<script>
import Vue from 'vue'
// eslint-disable-next-line
var staticRenderFns = [];
var dynamic = {
props: ['template'],
data: () => ({
templateRender: null,
}),
render(h) {
if (!this.templateRender) {
return h('div', 'loading...');
} else { // If there is a template, I'll show it
return this.templateRender();
}
},
watch: {
// Every time the template prop changes, I recompile it to update the DOM
template:{
immediate: true, // makes the watcher fire on first render, too.
handler() {
var res = Vue.compile(this.template);
this.templateRender = res.render;
// staticRenderFns belong into $options,
// appearantly
this.$options.staticRenderFns = []
// clean the cache of static elements
// this is a cache with the results from the staticRenderFns
this._staticTrees = []
// Fill it with the new staticRenderFns
for (var i in res.staticRenderFns) {
//staticRenderFns.push(res.staticRenderFns[i]);
this.$options.staticRenderFns.push(res.staticRenderFns[i])
}
}
}
},
}
export default {
name: 'App',
data: () => ({
template:`
<v-row>
<v-col>
<v-btn class="pa-2 primary white--text">Test</v-btn>
</v-col>
<v-col>
<v-btn class="pa-2 primary white--text">Test</v-btn>
</v-col>
<v-col>
<v-btn class="pa-2 primary white--text">Test</v-btn>
</v-col>
</v-row>
`,
}),
components:{
dynamic,
},
};
</script>
<template>
<v-app id="app" style="padding-top: 64px;">
<v-app-bar
app
color="blue"
>
<v-btn depressed color="white" class="black--text" click="addBtn">Button</v-btn>
</v-app-bar>
<dynamic :template='template'></dynamic>
</v-app>
</template>
Upvotes: 2
Views: 714
Reputation: 1
Inside the dynamic component try to render a vue component using the passed template :
var dynamic = {
props: ['template'],
data: () => ({
templateRender: null,
}),
render(h) {
if (!this.template) {
return h('div', 'loading...');
} else { // If there is a template, I'll show it
return h(Vue.component('dynamic-render', {template:this.template}));
}
},
}
Full Example
var dynamic = {
props: ['template'],
data: () => ({
templateRender: null,
}),
render(h) {
if (!this.template) {
return h('div', 'loading...');
} else { // If there is a template, I'll show it
return h(Vue.component('dynamic-render', {
template: this.template
}));
}
},
}
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
count: 1,
template: `
<v-row>
<v-col>
<v-btn class="pa-2 primary white--text">Test</v-btn>
</v-col>
<v-col>
<v-btn class="pa-2 primary white--text">Test</v-btn>
</v-col>
<v-col>
<v-btn class="pa-2 primary white--text">Test</v-btn>
</v-col>
</v-row>
`,
}),
components: {
dynamic,
},
methods: {
changeContent() {
this.count = this.count + 1
this.template = '';
setTimeout(() => { //simulate loading status
this.template = `<v-col>
<v-btn class="pa-2 primary white--text">Btn ${this.count}</v-btn>
</v-col>`
}, 2000);
}
}
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-btn depressed color="primary" class="black--text" @click="changeContent">change content</v-btn>
<dynamic :template='template'></dynamic>
</v-app>
</div>
Upvotes: 2