I want to bind values from input radio button generated by v-for.
I have tried use v-model to bind them with variables question_1, question_2, question_3 in data().
<template>
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="question_obj.variable" v-model="question_obj.variable" value="yes" >
<span>Yes</span>
</label>
<label>
<input type="radio" :name="question_obj.variable" v-model="question_obj.variable" value="no" >
<span>No</span>
</label>
</div>
</div>
</template>
<script>
export default {
name: 'radioButtons',
data () {
return {
question_1: '',
question_2: '',
question_3: '',
questions: [
{ question: 'Question 1', variable: 'question_1'},
{ question: 'Question 2', variable: 'question_2'},
{ question: 'Question 3', variable: 'question_3'},
]
}
}
}
</script>
I would like the value to be saved in data () after selecting the radio button.
Use index in v-for for v-model changes so it changes question object properties, not their instance:
new Vue({
el: '#app',
data () {
return {
//question_1: '', //don't need question_1, 2 and 3
// question_2: '',
// question_3: '',
questions: [
{ question: 'Question 1', variable: 'no'}, //notice that I have stored default values in question. Also consider renaming variable to answer to preserve good semantics.
{ question: 'Question 2', variable: 'yes'},
{ question: 'Question 3', variable: 'no'},
]
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="yes" :checked="question_obj.variable == 'yes'">
<span>Yes</span>
</label>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="no" :checked="question_obj.variable == 'no'">
<span>No</span>
</label>
</div>
</div>
</div>
Note: There is no need to store answers in question_1, question_2, question_3, since you might have more than 3 questions, and it won't be efficient.
Better way is to store answer in variable property value.
So in component it will look like this:
<template>
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="yes" :checked="question_obj.variable == 'yes'">
<span>Yes</span>
</label>
<label>
<input type="radio" :name="'question-' + index" v-model="questions[index].variable" value="no" :checked="question_obj.variable == 'no'">
<span>No</span>
</label>
</div>
</div>
</template>
<script>
export default {
name: 'radioButtons',
data () {
return {
//question_1: '', //don't need question_1, 2 and 3
// question_2: '',
// question_3: '',
questions: [
{ question: 'Question 1', variable: 'no'}, //notice that I have stored default values in question. Also consider renaming variable to answer to preserve good semantics.
{ question: 'Question 2', variable: 'yes'},
{ question: 'Question 3', variable: 'no'},
]
}
},
}
</script>
<style>
</style>
Try this code.
export default {
name: 'radioButtons',
data () {
return {
radio_data: {
'question_1': '',
'question_2': '',
'question_3': ''
},
questions: [
{ question: 'Question 1', variable: 'question_1'},
{ question: 'Question 2', variable: 'question_2'},
{ question: 'Question 3', variable: 'question_3'}
]
}
},
}
<template>
<div id="radioButtons">
<div v-for="(question_obj, index) in questions" :key="index" class="form-group form-radio">
<span>{{ question_obj.question }} {{ question_obj.variable }}</span>
<br>
<label>
<input type="radio" :name="'question-' + index" v-model="radio_data[questions[index].variable]" value="yes" :checked="question_obj.variable == 'yes'">
<span>Yes</span>
</label>
<label>
<input type="radio" :name="'question-' + index" v-model="radio_data[questions[index].variable]" value="no" :checked="question_obj.variable == 'no'">
<span>No</span>
</label>
</div>
</div>
</template>
Store your key, question, and answer in the same object in your array of questions. If you want to get the selected values as an object, use a method to reduce them into an appropriate value. In the example code below I've included such a method, as well as live JSON output to see the result.
<template>
<div id="radioButtons">
<div
v-for="row in questions"
:key="row.key"
class="form-group form-radio"
>
<span>{{ row.question }} {{ row.key }}</span>
<br />
<label>
<input
type="radio"
:name="row.key"
v-model="row.answer"
value="yes"
/>
<span>Yes</span>
</label>
<label>
<input
type="radio"
:name="row.key"
v-model="row.answer"
value="no"
/>
<span>No</span>
</label>
</div>
<pre>{{ JSON.stringify(getAnswers(), null, 2) }}</pre>
</div>
</template>
<script>
export default {
name: "radioButtons",
data() {
return {
questions: [
{ key: "question_1", question: "Question 1", answer: null },
{ key: "question_2", question: "Question 2", answer: null },
{ key: "question_3", question: "Question 3", answer: null },
],
};
},
methods: {
getAnswers() {
return this.questions.reduce((acc, cur) => {
acc[cur.key] = cur.answer;
return acc;
}, {});
},
},
};
</script>
Related
Looking for some tips on how to nest objects inside objects using a form. My form currently changes the key and value of an object. However, I'm now wanting a second button to be able to create a child (correct termanology?)form input. below you can see an example. I've spent the morning looking at props but I'm unsure if this is the correct way to go, any suggestions are greatly appriciated
{
"color": "black",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,255,1],
"hex": "#000"
}
},
<form id="app">
<h1>
Title goes here
</h1>
<hr>
<div class="row">
<div class="col-xs-2">
<button type="button" v-on:click="addNewObject" class="btn btn-block btn-success">
(Add +) Parent
</button>
</div>
<div class="col-xs-10 text_info">
Click 'Add +' to add an object
</div>
</div>
<div v-for="(object, index) in objects">
<div class="row">
<div class="col-xs-1">
<label> </label>
<button type="button" v-on:click="removeObject(index)" class="btn btn-rem btn-block btn-danger">
Delete
</button>
<button type="button" v-on:click="addNewChildObject()" class="btn btn-rem btn-block btn-success btn-suc">
add { }
</button>
</div>
<div class="form-group col-xs-7">
<div class="test">
<select v-model="object.type" class="selectBox classic">
<option value="" disabled selected hidden>Choose Datatype</option>
<option v-for="type in types"> {{ type }}</option>
</select>
<input v-model="object.name" :name="'objects[' + index + '][name]'" type="string" class="form-control" placeholder="Enter key">
<input v-model="object.dataValue" :name="'objects[' + index + '][dataValue]'" type="string" class="form-control" placeholder="Enter value">
</div>
</div>
</div>
</div>
<hr>
<div>
<pre v-if="seen">{{ mappedObjects }}</pre>
</div>
<button type="button" class="btn-primary" v-on:click="seen = !seen">{{ seen ? 'Click to Hide the JSON' : 'Click to Show the JSON' }}</button>
</form>
const getDefaultObject = () => ({
name: '',
dataValue: '',
type: ''
})
const app = new Vue({
el: '#app',
computed: {
mappedObjects() {
return this.objects.map(({
name,
dataValue,
type
}) => ({
[name]: dataValue,
type
}))
}
},
props: {
},
data() {
return {
seen: false,
types: ['string', 'character', 'number', 'int', 'floating-point', 'boolean', 'date;'],
objects: []
}
},
methods: {
addNewObject: function() {
this.objects.push(getDefaultObject())
},
removeObject: function(index) {
Vue.delete(this.objects, index);
},
addNewChildObject: function () {
}
}
})
If you want n forms with n children just create a model for it following that same structure and pass props to the form.
parent = {
...props,
children: []
}
child = {
...props
}
If the forms are too complex (or a little complex really), split them in separate components and pass children as props.
If you want to use the same form both in parent and children take a look at slots, they will allow you to create flexible layouts.
I generate so many input boxes on loop of article array.
article is array of objects. Input boxes values are set with "value" key in each object.
<div
v-for="(option, i) in this.article"
:key="i"
class="form-row"
>
<div class="col">
<input
v-model="option.value"
:label="'label '+i"
:name="'inputValue'+i"
type="text"
required
/>
</div>
</div>
<button #click="submit"></button>
<script>
export default {
name: 'Article',
data(){
return(
article:[
{id: 'art1', value: 'artValue1'},
{id: 'art2', value: 'artValue2'},
{id: 'art3', value: 'artValue3'}
// There are about 50 objects
]
)
},
methods:{
submit(){
// How get values of inputs?
}
}
}
</script>
How can I make the value of input change and update the object in vue?
I created a sample Single File Component based on your code. Look at my modifications. The 'magic' of Vue reactivity is that the 'article' data is updated in real time as you change the input values.
<template>
<div class="input-bound-object-array">
<div class="row">
<div class="col-md-6">
<form #submit.prevent="submitForm">
<div class="form-group" v-for="(option, i) in article" :key="i">
<label>{{ option.id }}</label>
<input type="text" class="form-control" v-model="option.value"
required />
</div>
<button type="submit" class="btn btn-secondary">Submit</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
article: [
{ id: 'art1', value: 'artValue1' },
{ id: 'art2', value: 'artValue2' },
{ id: 'art3', value: 'artValue3' }
// There are about 50 objects
]
}
},
methods: {
submitForm() {
// 'this.article' is updated as the input values are changed, so at this point
// you have the data changes, so you can process it as needed, i.e. POST to REST endpoint
console.log(this.article);
}
}
}
</script>
You forgot to return the object from data function and you can pass option to submit handler and print the input values.
// How get values of inputs? -> Do you mean all of the inputs or input where ever the button clicked or anything else. Below code is to handle individual input and submit handlers
<template>
<div
v-for="(option, i) in article"
:key="i"
class="form-row"
>
<div class="col">
<input
v-model="option.value"
:label="'label ' + i"
:name="'inputValue' + i"
type="text"
required
/>
</div>
<!-- <button #click="submit(option)">Submit</button> to handle individual inputs -->
</div>
<!-- <button #click="submitAll()">Submit All</button> to handle all inputs -->
</template>
<script>
export default {
name: 'Article',
data() {
return { // you missed this
article:[
{id: 'art1', value: 'artValue1'},
{id: 'art2', value: 'artValue2'},
{id: 'art3', value: 'artValue3'}
]
}
},
methods: {
submit(option) { // current option
console.log(option.id, option.value)
},
submitAll() {
console.log(this.article) // which will have update input values as
you're using `v-model`, check `values`
}
}
}
</script>
Demo Link here
I am creating a VueJs parent component that can create rows dynamically, and this component call another component which can populate 2 dropdowns with axios.
One for categories
The second one for subcategories (this dropdown can depend with the first one)
This is the first component for adding rows
<template>
<div>
<ul>
<li v-for="(input, index) in inputs" :key="index">
<request-part :index="index" :input="input" :inputs="inputs">
</request-part>
<hr />
</li>
</ul>
<button
type="button"
#click="addRow"
class="btn font-montserrat-regular btn-success btn-plus bt-radius-add"
>
Onderdeel toevoegen
</button>
</div>
</template>
<script>
export default {
data() {
return {
category: null,
selectedFile: null,
subcategory: null,
current: 0,
id: 0,
inputs: [],
categories: [],
subcategories: []
}
},
mounted() {
axios.get('/api/categories').then(res => {
this.categories = res.data
})
},
created() {
this.addRow()
},
methods: {
addRow() {
this.inputs.push({
category: '',
subcategory: '',
sku: '',
description: '',
image: ''
})
},
onFileChanged(event) {
this.selectedFile = event.target.files[0]
}
}
}
</script>
This is the second component for populating the dropdowns
<template>
<div class="border-0">
<div class="row">
<div class="col-md-8">
<div class="form-group ml-2">
<label class="gray-text-color font-montserrat-regular" :for="part">
{{ $t('labels.frontend.request.part') }} *
</label>
<div class="form-group brd3">
<select
:name="'r[' + index + '][category]'"
:id="category + index"
class="form-control light-gray-background arrow-select-position request-input"
v-model="input.category"
#change="onchangeCategorie"
required
>
<option :value="null" disabled selected>
{{ $t('labels.account.create.selectCategory') }}
</option>
<option
v-for="(option, index1) in categories"
:value="index1"
:key="index1"
>
{{ option }}
</option>
</select>
</div>
<div class="form-group brd3">
<select
:name="'r[' + index + '][subcategory]'"
:id="subcategory + index"
class="form-control light-gray-background arrow-select-position request-input"
v-model="input.subcategory"
required
>
<option :value="null" disabled selected>
{{ $t('labels.frontend.request.subCategory') }}
</option>
<option
v-for="(option, index1) in subcategories"
:value="index1"
:key="option.id"
>
{{ option }}
</option>
</select>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="gray-text-color font-montserrat-regular" :for="sku">
{{ $t('labels.frontend.request.articleNumber') }}
</label>
<input
type="text"
:name="'r[' + index + '][sku]'"
:id="'sku' + index"
v-model="input.sku"
class="form-control light-gray-background request-input"
/>
</div>
</div>
</div>
<div class="row">
<div class="col-9">
<div class="form-group" style="margin-right:-40px">
<input
type="text"
:name="'r[' + index + '][description]'"
v-model="input.description"
class="form-control light-gray-background input-width-mobile request-input"
placeholder="Toelichting (optioneel)"
/>
</div>
</div>
<input
:id="'image' + index"
:name="'r[' + index + '][image]'"
type="file"
class="camera-button inputfile"
:change="input.image"
accept="image/*"
#change="onFileChanged"
/>
<label :for="'image' + index">
<img
src="../../../../../resources/assets/images/cameraIcon.png"
alt="Camera icon"
class="camera-button-position"
/>
</label>
<div class="pr-l-200 ft-14 mr-3">
<label>{{ $t('labels.frontend.request.image') }}</label>
</div>
<div id="preview">
<img v-if="url" :src="url" alt="no Image." />
<button
v-if="url != null"
type="button"
#click="url = null"
class="btn fa fa-trash btn-default bt-radius"
></button>
</div>
</div>
<button
type="button"
#click="deleteRow(index)"
class="btn btn-danger fa fa-trash bt-radius"
></button>
</div>
</template>
<script>
export default {
props: {
part: {
type: String,
default: null
},
sku: {
type: String,
default: null
},
description: {
type: String,
default: null
},
image: {
type: String,
default: null
},
index: {
type: Number,
default: 0
},
input: {
type: Object,
default: () => ({})
},
inputs: {
type: Array,
default: () => []
}
},
data() {
return {
test: null,
category: null,
selectedFile: null,
subcategory: null,
categories: [],
subcategories: [],
url: null
}
},
mounted() {
axios.get('/api/categories').then(res => {
this.categories = res.data
})
},
methods: {
deleteRow(index) {
console.log(index)
this.$delete(this.inputs, index)
},
onFileChanged(event) {
this.selectedFile = event.target.files[0]
this.input.image = this.selectedFile
this.url = URL.createObjectURL(this.selectedFile)
},
onchangeCategorie(e) {
axios.get('/api/categories/' + e.target.value).then(res => {
this.subcategories = res.data
})
}
}
}
</script>
<style>
#preview {
display: flex;
justify-content: center;
align-items: center;
margin: auto;
}
#preview img {
max-width: 200px;
max-height: 200px;
border-radius: 5px;
border: 1px solid lightgray;
}
</style>
When i try to delete the first or any row from top to down, all the subcategories are gone.
When i delete the row from down to up it works fine
Your problem is caused by the fact that your rows don't have a proper stable unique ID, and that you're instead using their array index as the :key in your v-for directive. The reason this is a problem is that when you delete an element from an array using .$delete(), all the later elements get shifted down to a new, lower index so that the array remains contiguous.
The solution is to give your rows a unique ID. A simple global counter will do just fine:
var counter = 0; // global counter for row IDs (or anything else that needs one)
export default {
// ...
methods: {
addRow() {
this.inputs.push({
category: '',
subcategory: '',
sku: '',
description: '',
image: '',
id: ++counter // this gives each row a distinct ID number
})
},
// ...
}
Then you can use this unique ID as the :key in your v-for directive:
<li v-for="(input, index) in inputs" :key="input.id">
<request-part :index="index" :input="input" :inputs="inputs">
</request-part>
<hr />
</li>
I want two input field with name1 and name2, name1 = Peter, name2= Kitty. But i have not any idea to handle it.
<div v-for="list in item">
<input type="text" id="name" name="name" v-model="name"></>
{{list.name}}
</div>
data() {
return {
item: [{ name: 'Peter' },{ name: 'Kitty' }],
name: ''
}
}
here i want:
input type="text" id="name1" name="name2" v-model="name1"><--Show Peter
input type="text" id="name1" name="name2" v-model="name2"><--Show Kitty
The problem is with your v-model. You need to bind it to the name attribute of the object in your item array.
Change v-model="name" to v-model="list.name"
JSFiddle example
If you have multiple input elements you want to display, you should also have multiple slots of where you will put the data. For example, you could do it like this, by adding a value property to the item element:
new Vue({
el: '#app',
data: {
items: [
{ name: 'Peter', value: '' },
{ name: 'Kitty', value: '' }
],
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in items">
<label>{{item.name}}:</label>
<input type="text" name="item.name" v-model="item.value">
</div>
<!-- Output for debugging purposes -->
{{items}}
</div>
<div v-for="list in item">
<input type="text" v-model="list.name"></>
{{list.name}}
</div>
data() {
return {
item: [{ name: 'Peter' },{ name: 'Kitty' }],
name: ''
}
}
<div v-for="list in item" :key="list.id">
<input type="text" :id="list.name" :name="list.name" :v-model="list.name"/>
{{ list.name }}
</div>
How can i have checked radio button in v-for, if i my v-model is an array?
my data:
data() {
return {
persons: {
name: [],
surname: [],
sex: [],
}
}
}
and my radio:
<template v-for(person, person_index) in persons>
<div class="switch-sex">
<input type="radio" name="sex" :id="'male'+person_index" value="male"
v-model="persons.sex[person_index]">
<label :for="'male' + person_index">M</label>
<input type="radio" name="sex" :id="'female' + person_index"
value="female" v-model="persons.sex[person_index]">
<label :for="'female' + person_index">F</label>
</div>
</template>
I need my first radio ( male) be checked in each person inside v-for
If I don't misunderstood your question and your objective, your doing dynamic forms for multiple persons then try like this
Template
//n and index used for 0-based looping
<div v-for="(n, index) in noOfPersons" :key="index">
Person {{ index + 1 }}
<div class="switch-sex">
<input type="radio" :name="'sex'+(index+1)" value="male" v-model="persons[index].sex">
<label >Male {{index+1}}</label>
</div>
<div>
<input type="radio" :name="'sex'+(index+1)" value="female" v-model="persons[index].sex">
<label >Female {{index+1}} </label>
</div>
</div>
Script (just an example to show its checked)
data() {
return {
noOfPersons: 2,
persons: [
{name: '', surname: '', sex: 'male'},
{name: '', surname: '', sex: 'female'},
]
}
}
For those using Vuetify.js (it's different approach with v-model on the v-radio-group wrapper)
<v-radio-group v-model="persons[index].sex" :mandatory="false">
<v-radio label="Male" :value="1" color="blue"></v-radio>
<v-radio label="Female" :value="0" color="blue"></v-radio>
</v-radio-group>
Here's the Code Pen
NOTE. It is recommended to use binary (0/1) data like 0 for male or 1 for female or other numbers like 1/2 Database Storage / ISO 5218 or dummy variables. Here's explanation