I have implemented a contact formula where the siteadmin can edit the whole formula from his admin panel in with basic laravel and blade templates.
Now I would like to implement the same in inertiajs (vuejs).
I have made a tool that creates the necassery vue code. So now I would like to render this code on the server side or dynamically render it when the site is loaded.
I'm still not very familiar with vue but I have seen some libraries like vue3-runtime-template but they did not work.
The code I want to render looks like this:
<Field
classes="transition duration-500 ease-in-out opacity-100 text-center required">
<label>
Was ist das Thema Ihres Anliegens?
</label>
<div >
<div >
<Input type="radio" id="offer" name="category"
:value="'offer'"
classes="opacity-0 absolute left-1/2 peer"
v-model="form.category"
/>
<Label for="offer"
classes="px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full"
>
Fragen zum Angebot
</Label>
</div>
<div >
<Input type="radio" id="business" name="category"
:value="'business'"
classes="opacity-0 absolute left-1/2 peer"
v-model="form.category"
/>
<Label for="business"
classes="px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full"
>
Geschäftliche Anfrage
</Label>
</div>
<div >
<Input type="radio" id="technical" name="category"
:value="'technical'"
classes="opacity-0 absolute left-1/2 peer"
v-model="form.category"
/>
<Label for="technical"
classes="px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full"
>
Technische Probleme
</Label>
</div>
<div >
<Input type="radio" id="other" name="category"
:value="'other'"
classes="opacity-0 absolute left-1/2 peer"
v-model="form.category"
/>
<Label for="other"
classes="px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full"
>
Sonstiges
</Label>
</div>
</div>
</Field>
What I have already tried:
v-runtime-template, vue3-runtime-template, v-html and this. The probelem with the following approach is that I cannot render the htmlBefore and htmlAfter separately .
<Field v-for="(children, index) in fields" :key="index"
classes="transition duration-500 ease-in-out text-center"
:
>
<component :is="child.componentType" v-for="(child) in children" v-bind="child" />
</Field>
data() {
return {
fields: [
[
{
htmlBefore: '<label for="category">'
' Was ist das Thema Ihres Anliegens?'
' </label>'
' <div >'
' <div >',
componentType: "Input",
type: "radio",
id: "offer",
name: "category",
value: "offer",
classes: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "Label",
forInput: "offer",
classes: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Fragen zum Angebot",
htmlAfter: "</div>",
},
{
htmlBefore: '<div >',
componentType: "Input",
type: 'radio',
id: 'business',
name: 'category',
value: "business",
classes: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "Label",
forInput: "business",
classes: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Geschäftliche Anfrage",
htmlAfter: "</div>",
},
{
htmlBefore: '<div >',
componentType: "Input",
type: 'radio',
id: 'technical',
name: 'category',
value: "technical",
classes: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "Label",
forInput: "technical",
classes: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Technische Probleme",
htmlAfter: "</div>",
},
{
htmlBefore: '<div >',
componentType: "Input",
type: 'radio',
id: 'other',
name: 'category',
value: "other",
classes: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "Label",
forInput: "other",
classes: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Sonstiges",
htmlAfter: "</div> </div>",
},
],
[
{
componentType: "Textarea",
classes: "w-full h-80 resize-none !text-wonder-blue mt-2 rounded-2xl placeholder-shown:border-wonder-blue",
name: "message",
minLength: 5,
maxLength: 5000,
placeholder: "Hier haben Sie genug Platz, um mir Ihr Anliegen zu schildern",
model: this.form.message,
},
],
[
{
htmlBefore: '<div >'
' <div >',
componentType: "Input",
name: "firstname",
placeholder: "Vorname*",
classes: "mt-4",
model: this.form.firstname
},
{
componentType: "Input",
name: "lastname",
placeholder: "Nachname*",
classes: "mt-4",
model: this.form.lastname,
htmlAfter: "</div>",
},
{
componentType: "Input",
isRequired: false,
name: "company",
placeholder: "Unternehmen",
model: this.form.company,
},
{
componentType: "Input",
isRequired: false,
name: "website",
placeholder: "Website",
model: this.form.website,
},
{
componentType: "Input",
isRequired: false,
name: "phone",
placeholder: "Telefonnummer",
model: this.form.phone,
},
{
componentType: "Input",
type: "email",
name: "email",
placeholder: "E-Mail Adresse*",
model: this.form.email,
},
{
htmlBefore: '<div >',
componentType: "Input",
type: "hidden",
name: "subscribe_newsletter",
model: this.form.subscribe_newsletter,
classes: "hidden",
value: 0,
},
{
htmlBefore: '<div >',
componentType: "Input",
type: "checkbox",
name: "subscribe_newsletter",
model: this.form.subscribe_newsletter,
classes: "form-checkbox !px-2 !hover:ring-none",
value: 1,
isRequired: false,
},
{
componentType: "Label",
forInput: "subscribe_newsletter",
labelText: "Newsletter abonnieren",
htmlAfter: "</div>",
},
{
htmlBefore: '<div >',
componentType: "Input",
type: "checkbox",
name: "privacy_policy",
model: this.form.privacy_policy,
classes: "form-checkbox !px-2 !hover:ring-none",
value: 1,
isRequired: false,
},
{
componentType: "Label",
forInput: "privacy_policy",
labelText: "Datenschutzerklärung zustimmen*",
htmlAfter: "</div> </div> </div>",
},
],
],
},
},
CodePudding user response:
you can use v-html
in your vue file and put there html
you can see in vue doc
CodePudding user response:
I have solved the problem like this:
//Vue component to render dynamically created contact form fields
<Field v-for="(children, index) in fields" :key="index"
:
>
<variable-render v-for="(child) in children" v-bind="child"/>
</Field>
//contact form fields to render
fields: [
[
{
componentType: "FormLabel",
forInput: "category",
labelText: "Was ist das Thema Ihres Anliegens?"
},
{
componentType: "div",
class: "grid justify-center",
children: [
{
componentType: "div",
class: "flex mt-3 flex-row items-center space-x-4",
children: [
{
componentType: "FormInput",
type: "radio",
id: "offer",
name: "category",
value: "offer",
class: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "FormLabel",
forInput: "offer",
class: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Fragen zum Angebot",
},
]
},
{
componentType: "div",
class: "flex mt-3 flex-row items-center space-x-4",
children: [
{
componentType: "FormInput",
type: "radio",
id: "business",
name: "category",
value: "business",
class: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "FormLabel",
forInput: "business",
class: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Geschäftliche Anfrage",
},
]
},
{
componentType: "div",
class: "flex mt-3 flex-row items-center space-x-4",
children: [
{
componentType: "FormInput",
type: "radio",
id: "technical",
name: "category",
value: "technical",
class: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "FormLabel",
forInput: "technical",
class: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Technische Probleme",
},
]
},
{
componentType: "div",
class: "flex mt-3 flex-row items-center space-x-4",
children: [
{
componentType: "FormInput",
type: "radio",
id: "other",
name: "category",
value: "other",
class: "opacity-0 absolute left-1/2 peer",
model: this.form.category,
},
{
componentType: "FormLabel",
forInput: "other",
class: "px-6 hover:cursor-pointer md:px-52 py-3 rounded-lg border border-online-blue peer-checked:text-white peer-checked:bg-wonder-blue peer-hover:text-white peer-hover:bg-wonder-blue w-full",
labelText: "Sonstiges",
},
]
},
]
},
],
],
//VariableRender.vue
<script>
import {h, resolveComponent} from 'vue';
export default {
name: "VariableRender",
props: {
componentType: String,
children: {
type: Array,
default: undefined
},
},
methods: {
makeVNodeTree(childObject) {
if (Array.isArray(childObject)) {
let childArray = [];
for (let i = 0; i < childObject.length; i ) {
if (childObject[i].children !== undefined) {
let {componentType, children, route, makeVNodeTree, ...newChildObject} = childObject[i];
if(!this.tagIsValid(childObject[i].componentType)){
childArray.push(h(resolveComponent(childObject[i].componentType), newChildObject, this.makeVNodeTree(childObject[i].children)));
} else {
childArray.push(h(childObject[i].componentType, newChildObject, this.makeVNodeTree(childObject[i].children)));
}
} else{
let {componentType, route, makeVNodeTree, ...newChildObject} = childObject[i];
if(!this.tagIsValid(childObject[i].componentType)){
childArray.push(h(resolveComponent(childObject[i].componentType), newChildObject));
} else {
childArray.push(h(childObject[i].componentType, newChildObject));
}
}
}
return childArray;
}
if (childObject.children !== undefined) {
let {children, componentType, route, makeVNodeTree, ...newChildObject} = childObject;
if(!this.tagIsValid(childObject.componentType)){
return h(resolveComponent(childObject.componentType), newChildObject, this.makeVNodeTree(childObject.children));
}
return h(childObject.componentType, newChildObject, this.makeVNodeTree(childObject.children));
}
let {componentType, route, makeVNodeTree, children, ...newChildObject} = childObject;
if(!this.tagIsValid(childObject.componentType)){
return h(resolveComponent(childObject.componentType), newChildObject);
}
return h(childObject.componentType, newChildObject);
},
tagIsValid(tag) {
let tagChecked = document.createElement(tag).toString();
return tagChecked !== "[object HTMLUnknownElement]";
}
},
render() {
return this.makeVNodeTree(
{
componentType: this.componentType,
children: this.children
}
);
}
,
}
</script>