My question is if I am doing the below code correctly or is there a better way? I want to handle the change in any input values.
I have an object of options:
customOptions: {
1:"test 1",
2:"test 2",
3:"test 3",
}
and I am showing them in the template like this:
<div class="custom_options">
<div
class="row-fluid flex form-row"
v-for="(option, index) in customOptions"
:key="index"
>
<span>{{ index }}</span>
<span>
<input :value="option" :id="index" #change="handleChange" />
</span>
</div>
</div>
and finally my method:
handleChange(event) {
Vue.set(this.customOptions, event.target.id, event.target.value)
},
You could just use v-model like <input v-model="customOptions[index]"/> :
<div
class="row-fluid flex form-row"
v-for="(option, index) in customOptions"
:key="index"
>
<span>{{ index }}</span>
<span>
<input v-model="customOptions[index]"/>
</span>
</div>
The V-model:"question.answer" is the same for each loop.
The content of rating_questions is:
rating_question = [
{
"id":1,
"question":"How did you like this?",
"amount_of_stars":8,
"answer":0
},
{
"id":2,
"question":"Second question?",
"amount_of_stars":3,
"answer":0
}]
When I select an answer for the first question, the answer is saved in rating_question[0].answer but if I select an answer for the second question, it is also saved in rating_question[0].answer and not in rating_questions[1].answer as I would expect.
<template>
<div class="ratings">
<div class="rating" v-for="(question, index) in rating_questions">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars">
<input :id="i" name="rating" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="i" >☆</label>
</span>
{{ rating_questions[index]['answer'] }}
{{index}}
<div class="clear"></div>
</div>
</div>
</div>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendRating">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user', 'event', 'rating_questions'],
methods: {
sendRating() {
this.$emit('ratingsent', {
rating_questions: this.rating_questions
});
}
}
}
</script>
Your problem is not in Vue usage but how you use <input> and <label> HTML elements...
<input> id and <label> for attributes are assigned with simple number
1..question.amount_of_stars...which means first combo for every question will have id = 1, second 2 etc. Moreover you are using same name for every combo!
Now if you click on the label (star) in second question, browser just switch active combo on 1st question.
Try this:
<input :id="`rating-${question.id}-${i}`" :name="`rating-${question.id}`" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="`rating-${question.id}-${i}`" >☆</label>
Now:
every combo in the group (single question) will have same name (OK!)
every combo (and it's corresponding label) will have different id (OK!)
Also it's usually better to use :key together with v-for
new Vue({
data() {
return {
rating_questions: [
{
"id":1,
"question":"How did you like this?",
"amount_of_stars":8,
"answer":0
},
{
"id":2,
"question":"Second question?",
"amount_of_stars":3,
"answer":0
}]
}
}
}).$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="ratings">
<div class="rating" v-for="(question, index) in rating_questions" :key="question.id">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars">
<input :id="`rating-${question.id}-${i}`" :name="`rating-${question.id}`" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="`rating-${question.id}-${i}`" >☆</label>
</span>
{{ question.answer }}
<div class="clear"></div>
</div>
</div>
</div>
</div>
</div>
The error was in my input id's...
Each new question had the same id's.
<template>
<div class="ratings">
<div class="rating" v-for="(question, index) in rating_questions">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars">
<input :id="'rating' + index + i" name="rating" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="'rating' + index + i" >☆</label>
</span>
<div class="clear"></div>
</div>
</div>
</div>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendRating">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user', 'event', 'rating_questions'],
methods: {
sendRating() {
this.$emit('ratingsent', {
rating_questions: this.rating_questions
});
}
}
}
</script>
There is very trivial fix of this problem.
See you are modifying the props. In vuejs, props are not supposed to be updated. I hope you might have encountered some error in the console (not sure though, as sometimes in my case also it doesn't appear)
Please use this SFC file
<template>
<div class="ratings">
<!-- using the data variable rather than props -->
<div class="rating" v-for="(question, index) in questions" :key="index">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars" :key="i">
<input
:id="i"
name="rating"
v-model="question.answer"
type="radio"
:value="i"
class="radio-btn hide"
/>
<label :for="i">☆</label>
</span>
{{ questions[index]["answer"] }}
{{ index }}
<div class="clear"></div>
</div>
</div>
</div>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendRating">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ["user", "event", "rating_questions"],
data() {
return {
questions: this.rating_questions, // because you can not change props in vuejs
};
},
methods: {
sendRating() {
this.$emit("ratingsent", {
rating_questions: this.questions,
});
},
},
};
</script>
Consider the following:
<ul class="park-list row row-pd">
<li class="col-12" v-for="(item, index) in list.items" v-bind:key="index">
<div class="park-note px-3 py-2">
<p class="font-italic" >
{{ item.notes }} <a v-bind:href="'#collapseNoteTest'+item.id"
data-toggle="collapse" role="button" aria-expanded="false"
v-bind:aria-controls="'#collapseNoteTest'+item.id">
<i class="svg-12 ic-cyan-path" v-html="icons['ic-edit']"></i></a>
</p>
<div class="collapse" v-bind:id="'collapseNoteTest'+item.id">
<div class="form-group">
<label for="exampleFormControlTextarea1">Note:</label>
<textarea class="form-control" v-model="item.notes" rows="3"></textarea>
</div>
<div class="form-row">
<div class="col-auto ml-auto">
<button type="button" class="btn btn-outline-primary" v-bind:href="'#collapseNoteTest'+item.id" data-toggle="collapse">Cancel</button>
</div>
<div class="col-auto">
<button class="btn btn-success mb-2" v-bind:href="'#collapseNoteTest'+item.id" data-toggle="collapse">Save</button>
</div>
</div>
</div>
</div>
</li>
</ul>
No matter what I do here, item.notes does not update when I enter text into the textarea. What am I doing wrong?
i have simple questions list. It has some options and those options has some nested suboptions.
So need to keep hidden all nested checkboxes until checks parent checkbox.
i tried to do it but could not get it work.
i have tried this so far:
For better understanding please have a look at this fiddle
<div id="app">
<h2>Questions:</h2>
<ul>
<li v-for="question in questions">
<div class="question">{{ question.question }}</div>
<div class="label__wrap" v-for="option in question.options">
<label>
<input type="checkbox" v-model="question.answer" :value="option.option">
<span>
{{ option.option }}
</span>
</label>
<div class="sub__label__wrap" v-for="opt in option.subOptions">
<label>
<input type="checkbox" v-model="option.subOptsAnswers" :value=" opt.option">
<span>
{{ opt.option }}
</span>
</label>
</div>
</div>
<br><br>
</li>
</ul>
</div>
Thanks.
Updated Fiddle : https://jsfiddle.net/rcam67b9/
check the answers array whether the selected answer exist
v-if="question.answer.indexOf(option.option) !== -1"
Final Code
<div id="app">
<h2>Questions:</h2>
<ul>
<li v-for="question in questions">
<div class="question">{{ question.question }}</div>
<div class="label__wrap" v-for="option in question.options">
<label>
<input type="checkbox" v-model="question.answer" :value="option.option">
<span>
{{ option.option }}
</span>
</label>
<div class="sub__label__wrap" v-for="opt in option.subOptions" v-if="question.answer.indexOf(option.option) !== -1">
<label>
<input type="checkbox" v-model="option.subOptsAnswers" :value=" opt.option">
<span>
{{ opt.option }}
</span>
</label>
</div>
</div>
<br><br>
</li>
</ul>
</div>
I'm using Vue.js to add multiple rows, each row contain two datetimepicker inputs and a bootstrap-select.
My problem is when I fill the inputs and click to add a new row the previous ones clear out, the reason is each time I add a new row I'm using setTimeout to referesh the selectpicker and the datetimepicker.
So I want to know if there is a way to trigger the last added element them without refreshing the previous ones.
Here is my code :
HTML
<div class="cols" id="app">
<ul style="list-style-type: none;">
<li>
<button style="float: right;margin-right: 20px;margin-top: 5px;" type="button" class="btn btn-success btn-xs" v-on:click="addRow()">
<i class="fa fa-plus"></i> Ajouter
</button>
</li>
<li v-for="(row, id) in rows">
<div class="form-inline cp-out" style="padding-bottom: 9px;border-radius:4px;background-color:white;margin-left:-20px;display:inline-block;width:100%;margin-top:10px;">
<div class="col-md-3">
<label :for="['time_start'+id]" class="control-label">start time</label>
<div class="input-group date form_time" style="width:100%;" data-date="" data-date-format="hh:ii" :data-link-field="['time_start'+id]" data-link-format="hh:ii">
<input v-model="row.startTime" :name="'rows['+id+'][startTime]'" class="form-control" :id="['startTime' + id]" size="16" type="text" value="" >
<span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
</div>
</div>
<div class="col-md-3">
<label :for="['time_end'+id]" class="control-label" style="margin-left:7px;">end time</label>
<div class="input-group date form_time" style="width:100%;" data-date="" data-date-format="hh:ii" :data-link-field="['time_end'+id]" data-link-format="hh:ii">
<input v-model="row.endTime" :name="'rows['+id+'][endTime]'" class="form-control" :id="['endTime'+id]" size="16" type="text" value="" >
<span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group select_group" style="margin-left:5px;">
<label :for="['status' + id]" class="control-label">Status</label>
<select data-width="100%" v-model="row.status" :name="'rows['+id+'][status]'" :id="['status' + id]" class="select_picker" data-live-search="true" multiple >
<option value="Status 1">Status 1</option>
<option value="Status 2">Status 2</option>
</select>
</div>
</div>
<div class="col-xs-1">
<button type="button" class="btn btn_delete btn-xs btn-danger" v-on:click="delRow(id)">
<i class="fa fa-remove"></i> delete
</button>
</div>
</div>
</li>
</ul>
</div>
JS
app = new Vue({
el: '#app',
data: {
rows: []
},
methods: {
addRow: function () {
this.rows.push({startTime: '', endTime: '', status: []});
setTimeout(function () {
$('.select_picker').selectpicker('refresh');
$('.form_time').datetimepicker({format: 'LT'});
}.bind(this), 10);
},
delRow: function (id) {
this.rows.splice(id, 1);
}
},created:function() {
this.addRow();
}
});
This is the example : https://jsfiddle.net/rypLz1qg/9/
You really need to write a wrapper component. Vue expects to control the DOM and not to have you making DOM-changing things like the *picker calls at arbitrary times. However, a component gives you the opportunity to tell your component how to interact with the DOM in each of its lifecycle hooks so that its behavior can be consistent. See the JavaScript tab on the wrapper component example page.