How to enable / disable an input with a select VueJS - javascript

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>

Related

Vue Unchecking isn't removing data from array?

I have a function that pushes the networkAudience to the array when the checkbox is checked but when I unchecked it the same network Audience get pushed into the array again. Clicking the networkAudience should be removed when I uncheck the box.
How should I change my function so that a networkAudience is removed if it's unchecked?
new Vue({
el: '#app',
data: {
networkAudience: {}
selected:[]
},
methods: {
netToggle(networkAudience)
{
if(!this.selected.includes(networkAudience))
this.selected.push(networkAudience);
else
this.selected.splice(this.selected.indexOf(networkAudience), 1);
}
}
});
<div v-for="(networkAudience, index) in networkAudiences" : key="index">
<tr>
<input
class="form-check-input"
type="checkbox"
:checked="selected.includes(networkAudience)"
#click="netToggle(networkAudience)"
>
</tr>
</div>
This should only show one object because I unchecked a box but I end up with two objects. The unchecked box duplicates.
If I understood you correctly , try like following snippet:
new Vue({
el: '#app',
data() {
return {
networkAudiences: [{id:1, name:'aaa'}, {id:2, name: 'bbb'}, {id:3, name: 'ccc'}],
networkAudience: {},
selected: []
}
},
methods: {
netToggle(networkAudience) {
if(!this.selected.includes(networkAudience)) {
this.selected.push(networkAudience);
} else {
this.selected.splice(this.selected.indexOf(networkAudience), 1);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(networkAudience, index) in networkAudiences" :key="index">
<div>
<label :for="networkAudience.id">{{ networkAudience.name }}</label>
<input
class="form-check-input"
type="checkbox"
:id="networkAudience.id"
:value="networkAudiences[index]"
#input="netToggle(networkAudience)"
/>
</div>
</div>
{{selected}}
</div>

filtering table by selection : column, condition and value js

I want to filter the table in real time by the column, the condition and the value entered by the user, I am zero in js, i am using django and postgres. Table saved at postgres.
template
<form action="" method="get" class="inline">
<select name="column" class="form-control">
<option value="" selected disabled>Choose field</option>
<option value="title">Title</option>
<option value="quantity">Quantity</option>
<option value="distance">Distance</option>
</select>
<label for="table">Condition</label>
<select name="condition" class="form-control">
<option value="" selected disabled>Choose condition</option>
<option value="greater">greater</option>
<option value="contains">contains</option>
<option value="lower">lower</option>
<option value="equals">equals</option>
</select>
<input class="form-control" type="text" placeholder="search" id="search-text" onkeyup="tableSearch()">
<button type="submit">Apply</button>
</form>
thanks a lot for help)
could it work?
function myFunction() {
var input = document.getElementById("myInput");
var filter = input.value.toUpperCase();
var table = document.getElementById("myTable");
var tr = table.getElementsByTagName("tr");
var tds = tr.getElementsByTagName('td');
for (var i = 0; i < tr.length; i++) {
var firstCol = tds[0].textContent.toUpperCase();
var secondCol = tds[1].textContent.toUpperCase();
if (firstCol.indexOf(filter) > -1 || secondCol.indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
Did you try array.filter() method?
const data = [
{ title: 'spray', quantity: 40, distance: 300 },
{ title: 'pray', quantity: 10, distance: 150 },
{ title: 'grey', quantity: 23, distance: 100 }
];
const result = data.filter(each => each[quantity] < 23 );
console.log(result);
// expected output: Array [{ title: 'pray', quantity: 10, distance: 150 }]
you can use eval and dynamically change the condition too.
If you are using vue.js try something like following snippet:
new Vue({
el: '#demo',
data() {
return {
products: [
{title: 'pro 1', qty: 7, dist: 234},
{title: 'apro 1', qty: 3, dist: 456},
{title: 'cpro 1', qty: 2, dist: 231},
{title: 'bpro 1', qty: 8, dist: 145},
{title: 'upro 1', qty: 4, dist: 768},
],
conditions: ['greater', 'contains', 'lower', 'equals'],
selField: '',
selCond: '',
value: '',
filteredProds: []
}
},
computed: {
formFields() {
const fields = []
for(const key in this.products[0]) {
let temp = this.products[0][key]
let type = typeof temp
fields.push([key, type])
}
return fields
},
},
methods: {
filterProd() {
if (this.selField !== '' && this.selCond !== '' && this.value !== '') {
if (this.selField[1] === 'number') {
this.filteredProds = this.products.filter(p => {
if(this.selCond === 'equals') {
return p[this.selField[0]] === Number(this.value)
} else if(this.selCond === 'greater') {
return p[this.selField[0]] > Number(this.value)
} else if(this.selCond === 'lower') {
return p[this.selField[0]] < Number(this.value)
}
})
} else {
this.filteredProds = this.products.filter(p => {
if(this.selCond === 'contains') {
return p[this.selField[0]].includes(this.value)
}
})
}
}
this.selField = ''
this.selCond = ''
this.value = ''
},
clearFilters() {
this.filteredProds = this.products
}
},
mounted() {
this.filteredProds = this.products
}
})
ul {
list-style: none;
width: 300px;
}
li, .header {
display: flex;
justify-content: space-between;
}
.header {
font-weight: 600;
background: lightblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<form action="" method="get" class="inline">
<select name="column" class="form-control" v-model="selField">
<option value="" selected disabled>Choose field</option>
<option v-for="(field, i) in formFields" :key="i" :value="field">{{ field[0] }}</option>
</select>
<label for="table">Condition</label>
<select name="condition" class="form-control" v-model="selCond">
<option value="" selected disabled>Choose condition</option>
<option v-for="(cond, i) in conditions" :key="i">{{ cond }}</option>
</select>
<input class="form-control" type="text" placeholder="search" id="search-text" v-model="value">
<button type="submit" #click.prevent="filterProd">Apply</button>
<button type="submit" #click.prevent="clearFilters">All</button>
</form>
<ul class="header">
<li v-for="(field, i) in formFields" :key="i">
{{ field[0] }}
</li>
</ul>
<ul>
<li v-for="(prod, i) in filteredProds" :key="i">
<p>{{ prod.title }}</p>
<p>{{ prod.qty }}</p>
<p>{{ prod.dist }}</p>
</li>
</ul>
</div>

Render nested array objects of unknown depth VUE

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...

Filter array based on multiple options (Vue/JavaScript)

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>

Dynamically adding different components in Vue

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>

Categories