How can I render and show nested elements on the UI (from nested value[] field of object) which are generated dynamically when you select option (e.g. List) in parent element?
For example, I got this code below. When you create field and chose List option, you should see one more nested and so on, depth is unknown. How can I render this to show to user? Kind of v-for inside v-for doesnt seem to work. Maybe I need recursion here, but I dont know how to release it.
I appreciate any help!
var app = new Vue({
el: '.container',
data: {
modelname: '',
fields: []
},
methods: {
addNewField() {
this.fields.push({
left: 0,
type:'',
method:'',
size:'',
value:''}
)
},
createChildElement(field) {
if (field.type == "List") {
Vue.set(field, "value", []);
field.value.push({
type:'',
left: field.left+20,
method:'',
size:'',
value:''}
);
}
},
showJson() {
const data = this.fields
alert(JSON.stringify(data, null, 2));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container">
<h1 class="font-italic text-warning">TDGT Web Client</h1>
<!--<button id="add-model" class="btn btn-warning" #click="addNewModel">Создать модель</button>-->
<div>
<button id="add-field" class="btn btn-sm btn-warning" #click="addNewField">Create field</button>
<button id="show-json" class="btn btn-sm btn-warning" #click="showJson">Show JSON</button>
<div v-for="(field, index) in fields" :style="{marginLeft: field.left+'px'}" :key="index">
<ul>
<li>
<select v-model="field.type" v-on:change="createChildElement(field)" aria-label="Выбрать тип поля">
<option selected>Тип поля</option>
<option value="List">List</option>
<option value="Map">Map</option>
<option value="Integer">Integer</option>
</select>
<select v-model="field.method" v-if="field.type === 'Map' || field.type === 'List'" aria-label="Метод генерации">
<option selected>Тип значения</option>
<option value="Static">Static</option>
<option value="Random">Random</option>
<option value="Range">Range</option>
</select>
<input type="text" v-if="field.type === 'Map' || field.type === 'List'" v-model="field.size" placeholder="Размерность">
<input type="text" v-if="field.type === 'Integer'" v-model="field.value" placeholder="Значение">
</li>
<ul></ul>
</ul>
</div>
</div>
</div>
UPD. Based on answers I tried to make a solution for my task but I still have some problems. I moved most of the code to component, but I receive a lot of errors which I cant resolve. e.g.:
Invalid prop: type check failed for prop "item". Expected Object, got Array.
Property or method "fields" is not defined on the instance but referenced during render.
Here is my code:
Vue.component("el-inpt-group", {
template: "#item-template",
props: {
item: Object,
}
});
var app = new Vue({
el: '.container',
data: {
modelname: '',
fields: [
]
},
methods: {
addNewField() {
this.fields.push({
name: '',
left: 0,
type:'',
method:'',
size:'',
value:''}
)
},
createChildElement(field) {
if (field.type == "List") {
Vue.set(field, "value", []);
field.value.push({
name: '',
type:'',
left: field.left+20,
method:'',
size:'',
value:''}
)
}
},
showJson() {
const data = this.fields
alert(JSON.stringify(data, null, 2));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/x-template" id="item-template">
<ul>
<li v-for="field in fields">
<input type="text" v-model="field.name" placeholder="Name">
<select v-model="field.type" v-on:change="createChildElement(field)" aria-label="Выбрать тип поля">
<option selected>Тип поля</option>
<option value="List">List</option>
<option value="Map">Map</option>
<option value="Integer">Integer</option>
</select>
<select v-model="field.method" v-if="field.type === 'Map' || field.type === 'List'" aria-label="Метод генерации">
<option selected>Тип значения</option>
<option value="Static">Static</option>
<option value="Random">Random</option>
<option value="Range">Range</option>
</select>
<input type="text" v-if="field.type === 'Map' || field.type === 'List'" v-model="field.size" placeholder="Размерность">
<input type="text" v-if="field.type === 'Integer'" v-model="field.value" placeholder="Значение">
</li>
<ul><el-inpt-group v-for="child in fields.value" :style="{marginLeft: field.left+'px'}" :key="child.name" :item="child"></el-inpt-group></ul>
</ul>
</script>
</head>
<body>
<div class="container">
<h1 class="font-italic text-warning">TDGT Web Client</h1>
<button id="add-field" class="btn btn-sm btn-warning" #click="addNewField">Create field</button>
<button id="show-json" class="btn btn-sm btn-warning" #click="showJson">Show JSON</button>
<el-inpt-group :item="fields"></el-inpt-group>
</div>
</div>
Indeed recursion is your solution here.
When you can't see how to organise your code properly, often that means you haven't divided your code into enough components.
To make a simple example, just create a component that takes one item as a property, and then call itself when this item as sub items.
Given this example structure:
[
{
name: 'Todo 1'
},
{
name: 'Todo 2',
items: [
{
name: 'Todo 2.1'
},
{
name: 'Todo 2.2'
}
],
},
{
name: 'Todo 3',
items: []
},
]
<template>
<div>
<article v-for="subTodos of todo.items" :key="subTodos.name">
<h1>{{ subTodos.name }}</h1>
<Todo :todo="subTodos" />
</article>
</div>
</template>
<script>
export default {
name: 'Todo',
props: {
todo: { type: Object, required: true }
}
}
</script>
<template>
<Todo :todo="firstItem" />
</template>
<script>
export default {
data () {
return {
firstItem: { name: 'First todo', items: nestedTodos }
}
}
}
</script>
Here, as long as todo.items isn't an empty array, it will create a <Todo> component, which himself creates more <Todo> elements whenever they have items themselves...
Related
I'm trying to append new input fields based on a condition, I will describe the workflow to help you understand
First stage is to press this button to implement 2 functions(1 is to move to other fieldset in the staged form, second function is to append the inputs:
<input type="button" name="secondBtn" class="next action-button" value="Next" id="secondBtn" #click="nextPlusappend"/>
nextPlusappend:
nextPlusappend() {
this.isNextClicked();
this.appendFields();
}
appendFields:
//this.platform initllized as 'one' so the condition is true.
if(this.platform === 'one'){
this.inputFields.push({
Username: '',
Password: '',
AffiliateID: '',
CI: '',
GI: '',
})
}
And I want to append all the fields from the function to this fieldset:
<div v-if="currentPage === 2">
<fieldset id="fieldset3" v-for="(param, index) in inputFields" :key="index">
<h2 class="fs-title">API Credentials</h2>
<h3 class="fs-subtitle">Step 3- Add any parameter for the integration</h3>
<input v-model="param.Username" type="text" name="`inputFields[${index}[Username]]`" placeholder="userName">
<input type="button" name="previous" class="previous action-button" value="Previous" #click="isPreviousClicked"/>
<input type="submit" name="submit" class="submit action-button" value="Create a Ticket" id="excel"/>
</fieldset>
</div>
How can I append this without hard code all the input fields as I did here:?
<input v-model="param.Username" type="text" name="`inputFields[${index}[Username]]`" placeholder="userName">
This is designated to be dynamic, what do i mean?
I mean that if the this.platform is equal to "one" there will be a unique fields, and if this.platform equal to "two" for example there will be other unique fields.
Don't think like "pushing a form field", rather think like "adding a new item to the dataset" (and of course, its displayed UI is a form field).
Let me give an example:
Vue.component("FormField", {
props: ["label", "value"],
computed: {
val: {
get() {
return this.value
},
set(val) {
this.$emit("update:value", val)
}
},
},
methods: {
handleClickAdd() {
this.$emit("click-add-field")
}
},
template: `
<div>
<label>
{{ label }}: <input type="text" v-model="val" />
</label>
<button
#click="handleClickAdd"
>
+ ADD
</button>
</div>
`,
})
new Vue({
el: "#app",
data() {
return {
formFields: [{
label: "Field 1",
value: null,
}],
}
},
methods: {
handleClickAddField() {
const item = {
label: `Field ${ this.formFields.length + 1 }`,
value: null,
}
this.formFields = [...this.formFields, item]
},
},
template: `
<div
class="container"
>
<div
class="col"
>
<h4>FIELDS:</h4>
<hr>
<form-field
v-for="(field, i) in formFields"
:key="i"
:label="field.label"
:value.sync="field.value"
#click-add-field="handleClickAddField"
/>
</div>
<div
class="col"
>
<h4>FIELD VALUES:</h4>
<hr>
<div
v-for="(field, i) in formFields"
:key="i"
>{{ field.label }}: {{ field.value }}</div>
</div>
</div>
`,
})
.container {
display: flex;
}
.col {
padding: 0 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
You can see, that on ADD I just added a new item in the formFields - the values are bound in the template to a child-component, that handles the actual representation of the fields.
On the right side of the snippet, you can see another benefit of decoupling data from UI: I created another representation of the same data source - that immediately reacts to any changes!
My values on my vue js form are blank or undefined when i submit them for some reason. I am trying to log e.target.name.value to the console to check my form values.
Is there an easier way to do this in vue for this setup? I've been trying different methods and I fear i'm trying to homogenize my vanilla js with vue and creating more problems for myself.
my vue data:
new Vue({
el: '#grocery_list',
data: {
selected: null,
list: [
{
id: 0,
category: 'Baked goods',
food: ['bread','cookie','butter','powder']
},
{
id: 1,
category: 'meats',
food: ['chicken','turkey','beef']
},
{
id: 2,
category: 'fruits',
food: ['bannana','apple','pineapple']
},
{
id: 3,
category: 'canned goods',
food: ['tomatoes','green beans','corn']
},
{
id: 4,
category: 'veggies',
food: ['broccoli','celery','lettuce']
},
{
id: 5,
category: 'pantry',
food: ['broom','mop','dried beans']
},
],
isHidden: true,
form: {},
},
methods:{
addItem: function(e){
console.log(form)
},
viewItemsinCat: function(){
this.isHidden = false
},
checkItem: function(){
}
}
})
my html
<div class="addItem" id="addItem" #submit.prevent="addItem">
<form>
<label for="addItem">Add food item</label>
<input v-model="form.foodName" type="text" name="foodName"></input>
<div>
<select v-model="form.category" id="food">
<option v-for="item in list" name="item">{{item.category}}</option>
</select>
</div>
<div>
<button type="submit">Add Item</button>
</div>
</form>
</div>
In vuejs you don't have to use the names or ids to get value of an input all you have is to use v-model which is reactive and will update all the form values instantly
check this out
<template>
<div class="addItem" id="addItem" #submit.prevent="addItem">
<form>
<label for="addItem">Add food item</label>
<input v-model="form.foodName" type="text" name="foodName"></input>
<div>
<select v-model="form.category" id="food">
<option v-for="item in list" name="item">{{item.category}}</option>
</select>
</div>
<div>
<button type="submit">Add Item</button>
</div>
</form>
</div>
</template>
<script>
export default {
data: () => ({
form: {}
}),
methods: {
addItem: function(){
console.log(this.form)
}
}
}
</script>
Question originally posted (in Spanish) on es.stackoverflow.com by José:
I have seen the example in JavaScript, and yes, it works and
everything but as I can do it in vue.js, I've been trying for a
while and it does not work. Sorry for the inconvenience.
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ name }}</p>
<input type="text" v-model="name"/>
<button type="submit" :disabled="name == defaultName">Submit</button>
</div>
>
Javascript
$(document).ready(function() {
$('#id_categoria').change(function(e) {
if ($(this).val() === "1") {
$('#d').prop("disabled", true);
} else {
$('#d').prop("disabled", false);
}
})
});
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div>
<select name='id_categoria' id='id_categoria' >
<option value="1" selected>Clientes</option>
<option value="2">Empresas</option>
<option value="3">Personas</option>
</select>
<input id="d" disabled="disabled" type="text" value="test">
</div>
I know it's weird that I want to directly manipulate the DOM when you use Vue; I usually let the framework do that, This would be a possible solution
Of course, if it's a possibilty, you should take advantage of Vue's data-driven reactive nature, as tackling with the DOM is always tricky.
The solution would be to just create another variable and populate it on
new Vue({
el: '#app',
data: {
name: 'Hello Vue.js!',
defaultName: null
},
mounted() {
this.defaultName = this.name;
}
Below is a vue template file using v-model instead of combining jQuery
<template>
<div>
<select v-model="id_categoria">
<option value="1" :selected="id_categoria === 1">Clientes</option>
<option value="2" :selected="id_categoria === 2">Empresas</option>
<option value="3" :selected="id_categoria === 3">Personas</option>
</select>
<input id="d" :disabled="id_categoria === 1" type="text" value="test">
</div>
</template>
<script>
export default {
data() {
return {
id_categoria: 1,
};
},
}
</script>
Try this:
new Vue({
el: "#app",
data: {
options: [
{ content: 'Clientes', value: 1 },
{ content: 'Empresas', value: 2 },
{ content: 'Personas', value: 3 }
],
inputDisabled: true
},
methods: {
onChangeSelect(e) {
this.inputDisabled = (e.target.value == 1)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select #change="onChangeSelect($event)">
<option v-for="(option, index) in options" :key="index" :value="option.value">
{{ option.content }}
</option>
</select>
<input :disabled="inputDisabled" type="text" value="test">
</div>
new Vue({
el: "#app",
data: {
options: [
{ content: 'Clientes', value: 1 },
{ content: 'Empresas', value: 2 },
{ content: 'Personas', value: 3}
],
selectedOption: '',
},
computed: {
inputDisabled() {
return this.selectedOption === 2;
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<select v-model="selectedOption">
<option v-for="(option, index) in options" :key="index" :value="option.value">{{ option.content }}</option>
</select>
<input :disabled="inputDisabled" type="text" v-model="selectedOption">
</div>
If this is a duplicate post I apologize. I searched a couple different things before posting this. I'm trying to figure out how to filter results based off of two options. I can get one of the options to work but need both to drive the results.
I have 4 computed properties:
filteredResults: where the filtering is taking place
phases: collection of all the phases based on the results
results: original results list (stored in Vuex)
states: collection of all the states based on the results
In the data on that component I have two properties that are binded to what the user selects. I'm running the filtering off of those selected values.
Code
<template>
<div class="results">
<Banner/>
<div class="container">
<div class="columns">
<main role="main" class="column is-8-tablet is-9-widescreen">
<p class="title is-4">Results for: {{ $route.query.q }}</p>
<p class="subtitle">{{ results.length }} Trials Avaliable</p>
<ul>
<li v-for="trial in results" :key="trial.studyid">
<div class="card" :data-location="trial.state" :data-phases="trial.phasename">
<div class="card-content">
<div class="content">
<h2 class="title is-4">
<a href="void:javascript(0)" #click="goToDetail(trial.studyname)">
{{ trial.studyname }}
</a>
</h2>
<p>{{ trial.protocoltitle.replace('�', '') }}</p>
<p>Available in {{ trial.studyentitylocation.split('`').length -1 }} location(s)</p>
</div>
</div>
</div>
</li>
</ul>
</main>
<aside role="complementary" class="column is-4-tablet is-3-widescreen">
<p class="title is-4">Filter Options</p>
<button class="accordion">Locations</button>
<div class="panel">
<form>
<div class="control">
<label class="radio">
<input type="radio" name="states" value="All" v-model="checkedLocations">
All
</label>
<label class="radio" v-for="(state, i) in states" :key="i">
<input type="radio" name="states" :value="state" v-model="checkedLocations">
{{ state }}
</label>
</div>
</form>
</div>
<button class="accordion">Phase</button>
<div class="panel">
<form>
<div class="control">
<label class="radio">
<input type="radio" name="phases" value="All" v-model="checkedPhases">
All
</label>
<label class="radio" v-for="(phase, i) in phases" :key="i">
<input type="radio" name="phases" :value="phase" v-model="checkedPhases">
Phase {{ phase }}
</label>
</div>
</form>
</div>
</aside>
</div>
</div>
</div>
</template>
<script>
import Banner from '#/components/Banner'
export default {
name: 'Results',
components: {
Banner
},
data () {
return {
checkedLocations: 'All',
checkedPhases: 'All'
}
},
mounted () {
this.activateAccordion()
},
computed: {
results () {
return this.$store.state.results
},
states () {
let statesArray = []
this.results.forEach((result) => {
if (result.state) {
var state = result.state
state.forEach((item) => {
if (statesArray.indexOf(item) === -1) {
statesArray.push(item)
}
})
}
})
return statesArray.sort()
},
phases () {
let phaseArray = []
this.results.forEach((result) => {
if (result.phasename) {
var phase = result.phasename
phase.forEach((item) => {
if (phaseArray.indexOf(item) === -1) {
phaseArray.push(item)
}
})
}
})
return phaseArray.sort()
},
filteredResults () {
let results = ''
if (this.checkedLocations !== 'All') {
results = this.results.filter((result) => result.state.includes(this.checkedLocations))
return results
} else {
return this.results
}
}
}
}
</script>
Here is what the app looks like on the front end
Trials App
I'm also new to the modern JavaScript syntax so please be nice lol.
If i understand you, it Seems like you can just add another filter according to the checkedPhases, similar to the filter you already have?
I have created a codepen to try to show how you can achieve it. There is a list of items containing carID and cityID. Also, there are two selects to filter the result. When you change a select, it will filter the options of the other select as well. I hope it can help you, if you have any question, just ask.
const data = [
{
cityID: 1,
carID:1,
name: 'Ted'
},
{
cityID: 1,
carID:2,
name: 'Tod'
},
{
cityID: 2,
carID:1,
name: 'Michel'
},
{
cityID: 3,
carID:1,
name: 'Romeu'
},
{
cityID: 2,
carID:3,
name: 'Thomas'
},
{
cityID: 3,
carID:4,
name: 'Lucy'
},
{
cityID: 4,
carID:1,
name: 'Mary'
},
]
const cities = [{ cityID: 1, name:'New York'},{ cityID: 2, name:'Sydney'}, { cityID: 3, name:'Chicago'},{ cityID: 4, name:'Perth'}]
const cars = [{ carID: 1, name:'Cruze'},{ carID: 2, name:'Mustang'}, { carID: 3, name:'Blazer'},{ carID: 4, name:'Tucson'}]
new Vue({
el: '#app',
data: function() {
return {
data,
cities,
cars,
city: "",
car: "",
}
},
methods: {
findCarName: function (carID) {
return this.cars.find(car => car.carID === carID).name
},
findCityName: function (cityID) {
return this.cities.find(city => city.cityID === cityID).name
},
reset: function () {
this.city = ""
this.car = ""
}
},
computed: {
filteredData: function() {
let resultData = this.data
if (this.city) {
resultData = resultData.filter(item => item.cityID === this.city)
}
if (this.car) {
resultData = resultData.filter(item => item.carID === this.car)
}
return resultData
},
filteredCars: function () {
const carIDs = this.filteredData.reduce((acc, next) => {
if (acc.indexOf(next.carID) === -1){
return [...acc, next.carID]
}
return acc
},[])
if (carIDs.length) {
return carIDs.map(carID => ({carID, name: this.findCarName(carID)}))
}
return this.cars
},
filteredCities: function () {
const citiesIDs = this.filteredData.reduce((acc, next) => {
if (acc.indexOf(next.cityID) === -1){
return [...acc, next.cityID]
}
return acc
},[])
if (citiesIDs.length) {
return citiesIDs.map(cityID => ({cityID, name: this.findCityName(cityID)}))
}
return this.cities
}
}
})
#app {
margin: 30px;
}
#app .form-group {
display: flex;
align-items: center;
}
#app .form-group label {
font-weight: bold;
color: #337ab7;
margin-right: 20px;
}
#app .filters {
margin-bottom: 20px;
display: flex;
width: 700px;
justify-content: space-around;
}
#app .table {
width: 700px;
}
#app .table thead tr td {
font-weight: bold;
color: #337ab7;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="filters row">
<div class="form-group">
<label for="city">City</label>
<select v-model="city" id="city" class="form-control">
<option value="">Select City</option>
<option v-for="item in filteredCities" :key="item.cityID" :value="item.cityID">{{item.name}}</option>
</select>
</div>
<div class="form-group">
<label for="car">Car</label>
<select v-model="car" id="car" class="form-control">
<option value="">Select Car</option>
<option v-for="item in filteredCars" :key="item.carID" :value="item.carID">{{item.name}}</option>
</select>
</div>
<div class="form-group">
<button type="button" class="btn btn-primary" #click="reset">Reset</button>
</div>
</div>
<table class="table table-striped table-bordered">
<thead>
<tr><td>Name</td><td>Car</td><td>City</td></tr>
</thead>
<tbody>
<tr v-for="item in filteredData" :key="data.name">
<td>{{item.name}}</td>
<td>{{findCarName(item.carID)}}</td>
<td>{{findCityName(item.cityID)}}</td>
</tr>
</tbody>
</table>
<div>
I want to create a simple form builder with Vue where users click on buttons from a menu to add different form fields to a form. I know that if there was just one type of form field to add, I could do it with something like this (https://jsfiddle.net/u6j1uc3u/32/):
<div id="app">
<form-input v-for="field in fields"></form-input>
<button type="button" v-on:click="addFormElement()">Add Form Element</button>
</div>
<script type="x-template" id="form-input">
<div>
<label>Text</label>
<input type="text" />
</div>
</script>
And:
Vue.component('form-input', {
template: '#form-input'
});
new Vue({
el: '#app',
data: {
fields: [],
count: 0
},
methods: {
addFormElement: function() {
this.fields.push({type: 'text', placeholder: 'Textbox ' + (++this.count)});
}
}
})
But what if there's more than one type of form field (input, file, select, etc...)? I was thinking maybe build a different component for each type, but then how would I show multiple types of components in a single list of form elements?
Could I maybe create a component with children components of different types based on the data in the fields array?
Or is there a better way to go about this situation that I'm missing? I've just started learning Vue, so any help is appreciated!
Ok, so I looked into dynamic elements and managed to pull this together:
Vue.component('form-input', {
template: '#form-input'
});
Vue.component('form-select', {
template: '#form-select'
});
Vue.component('form-textarea', {
template: '#form-textarea'
});
new Vue({
el: '#app',
data: {
fields: [],
count: 0
},
methods: {
addFormElement: function(type) {
this.fields.push({
'type': type,
id: this.count++
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<component v-for="field in fields" v-bind:is="field.type" :key="field.id"></component>
<button type="button" v-on:click="addFormElement('form-input')">Add Textbox</button>
<button type="button" v-on:click="addFormElement('form-select')">Add Select</button>
<button type="button" v-on:click="addFormElement('form-textarea')">Add Textarea</button>
</div>
<script type="x-template" id="form-input">
<div>
<label>Text</label>
<input type="text" />
</div>
</script>
<script type="x-template" id="form-select">
<div>
<label>Select</label>
<select>
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</script>
<script type="x-template" id="form-textarea">
<div>
<label>Textarea</label>
<textarea></textarea>
</div>
</script>
So instead of creating a new form-input component for each item in the fields array, I'm creating a new component that is associated with the correct component via the type property of the fields
You can pass the field object as props of your form-input component and make the type dynamic:
Vue.component('form-input', {
template: '#form-input',
props: ['field']
})
new Vue({
el: '#app',
data: {
fields: [],
inputType: '',
count: 0
},
methods: {
addFormElement(val) {
this.fields.push({type: val, placeholder: 'Textbox ' + (++this.count)});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<h3>Add form element</h3>
<select size="3" v-model='inputType' #click="addFormElement(inputType)">
<option value="text">Text</option>
<option value="checkbox">Checkbox</option>
<option value="radio">Radio</option>
</select>
<p>
<form-input v-for="field in fields" :field="field"></form-input>
</p>
</div>
<template id="form-input">
<div>
<label>{{ field.type }}</label>
<input :type="field.type" />
</div>
</template>
Based on the code from the answer, one could add dynamic content for each one of those form controls as well ( the full concept could be seen from the following site):
Vue.component('form-input', {
template: '#form-input'
, props: ['label','cnt']
});
Vue.component('form-select', {
template: '#form-select'
, props: ['label','cnt']
});
Vue.component('form-textarea', {
template: '#form-textarea'
, props: ['label','cnt']
});
new Vue({
el: '#app',
data: {
fields: [],
count: 0
}
, mounted() {
// fetch those from back-end
this.addFormElement('form-input','lbl', "form-input-content")
this.addFormElement('form-textarea','lbl', "form-textarea-content")
var select_cnt = [
{'value': 1, 'text': 'item-01'},
{'value': 2, 'text': 'item-02'}
]
this.addFormElement('form-select','some-label',select_cnt)
}
, methods: {
addFormElement: function(type,label,cnt) {
this.fields.push({
'type': type
, id: this.count++
, 'label':label
, 'cnt':cnt
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<component v-for="field in fields" v-bind:is="field.type" :key="field.id" :cnt="field.cnt" :label="field.label"></component>
</div>
<script type="x-template" id="form-input">
<div v-on:keyup.tab="this.document.execCommand('selectAll',false,null);">
<label>{{label}}</label>
<input type="text" :value="cnt"/>
</div>
</script>
<script type="x-template" id="form-textarea">
<div v-on:keyup.tab="this.document.execCommand('selectAll',false,null);">
<label>{{label}}</label>
<textarea :value="cnt"></textarea>
</div>
</script>
<script type="x-template" id="form-select">
<div>
<label>Select</label>
<select>
<option v-for="oitem in cnt" :value="oitem.value">{{oitem.text}}</option>
</select>
</div>
<div v-html="cnt"></div>
</script>