Reputation: 13
Good day Community.
I need your help to build HTML controls dynamically in VUE from a JSON.
Currently I have an exercise done, I have an html template that renders a list of input controls from the parameters set in a JSON. However I want to build more controls, not just inputs controls.
export default {
name: 'app',
data () {
return {
msg: 'Hola Mundo',
fields: [
{
"name": "fechaRegistro",
"label": "Fecha de Registro",
"type": "date",
"placeholder": 'Ingresa Fecha'
},
{
"name": "nombreDeUsuario",
"label": "Nombre de Usuario",
"type": "text",
"placeholder": "Ingresa Usuario"
},
{
"name": "passwordUsuario",
"label": "Password",
"type": "password",
"placeholder": "Contraseña"
},
{
"name": "adjuntarArchivo",
"label": "Adjuntar",
"type": "file"
},
{
"name": "activo",
"label": "Activo",
"type": "checkbox"
},
{
"name": "roles",
"label": "Roles",
"type": "multiSelect",
"sortedByKey": false,
"options": [{
"name": "admin",
"label": "Admin"
},
{
"name": "user",
"label": "User"
},
{
"name": "guest",
"label": "Invitado"
}
]
}
]
}
}
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
<template>
<div id="app">
<h1>{{msg}}</h1>
<form>
<ul id="controles">
<li v-for="field in fields" :key="field">
<label :for="field.name">{{field.label}}</label>
<input :id="field.name" :type="field.type" :placeholder="field.placeholder">
</li>
</ul>
</form>
</div>
</template>
My objective is to receive in the Json the type of control that I should build along with its parameters. For example, if a select type control comes in the json, I must build a select, if a textarea type control comes, I must build a textarea and so on.
Leave your solution ideas please, thank you.
Upvotes: 1
Views: 617
Reputation: 1143
You would need to know in advance which field types to expect. You could create a method hasInputTag
like this:
methods: {
hasInputTag(type) {
return ['text', 'password', 'file', 'date'].includes(type);
}
}
Then check the field.type
inside your v-for
loop and render the appropriate element, e.g.:
<li v-for="field in fields" :key="field">
<label :for="field.name">
{{ field.label }}
</label>
<input
v-if="hasInputTag(field.type)"
:name="field.name"
:type="field.type"
>
<select
v-else-if="field.type === 'select'"
:name="field.name"
>
<option v-for="opt in field.options">
// etc.
</option>
</select>
<textarea
v-else-if="field.type === 'textarea'"
:name="field.name"
>
</li>
UPDATE 1:
If you wanted to use separate components, you could do something like this in your template:
<component
:is="getComponentName(field.type)"
v-bind="field"
/>
And your getComponentName
method could look like this (where each component has a name such as Custom_input
, Custom_checkbox
, etc. and you have imported them in the parent):
getComponentName(type) {
if (['text', 'password', 'file', 'date'].includes(type)) return 'Custom_input';
return `Custom_${type}`;
}
UPDATE 2:
To store the values of each component, I would create a fieldValues
object in the parent and initialise it like this:
fieldValues: this.fields.reduce((res, field) => {
res[field.name] = null;
return res;
}, {})
Then use v-model="fieldValues[field.name]"
in your loop.
Upvotes: 2