VUEJS: Model not changing in for loop - vue.js

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>

Related

Nested Scoped Slots Variable Collisions VueJS

I've created a simple scoped slot component that I need to nest, but I'm struggling to figure out how I can avoid naming collisions.
Vue Component nested-fields
<script>
export default {
props: [
"entityName",
"items"
],
data: function() {
return {
formItems: this.items
}
},
methods: {
addItem: function() {
this.items.push({})
},
removeItem: function(index) {
if (this.items[index].id) {
this.$set(this.items[index], '_destroy', true);
} else {
this.items.splice(index, 1);
}
}
}
}
</script>
<template>
<div class="nested-fields">
<div v-show="item._destroy !== true || typeof item._destroy == 'undefined'" class="card nested-fields__field-set mb-2" v-for="(item, index) in formItems" :key="index">
<div class="card-header d-flex justify-content-between">
<span>Add {{entityName}}</span> <span class="fa fa-times" #click="removeItem(index)"></span>
</div>
<div class="card-body">
<slot name='item-fields' v-bind="{item, index}"></slot>
</div>
</div>
<button class="btn btn-primary btn-xs mb-2" type="button" #click="addItem()">Add {{entityName}}</button>
</div>
</template>
HTML
<nested-fields entity-name="Rotap Analysis" :items="[]">
<template #item-fields="{item, index}">
<div class="form-group col-sm-4">
<label>
Amount (g)
<input type="number" min="0" v-model="item.amount_grams" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][amount_grams]'" class="form-control">
</label>
</div>
<div class="form-group col-sm-4">
</div>
<div class="form-group col-sm-4">
</div>
<nested-fields entity-name="Sieve" :items="[]">
<template #item-fields="{item2, index2}">
<label>
Sieve Size (US Mesh)
<input type="number" min="0" v-model="item2.size_mesh" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][rotap_sieves]['+index2+'][size_mesh]'" class="form-control">
</label>
</template>
</nested-fields>
</template>
</nested-fields>
I need to rename the variables in the nested template shown here:
<nested-fields entity-name="Sieve" :items="item.rotap_sieves || []">
<!-- this line --><template #item-fields="{item2, index2}">
So I can use them here:
<input type="number" min="0" v-model="item2.size_mesh" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][rotap_sieves]['+index2+'][size_mesh]'" class="form-control">
... BUT it does not let me rename the destructuring as I have it from "item" and "index" to "item2" and "index2".
For what it's worth, I'm attempting to replace Cocoon rails gem for nesting forms in my Rails app, though that shouldn't really matter.
Question - How can I rename the variables in nested scoped slots to avoid variable collisions?
I figured out it's a simple destructuring syntax solution:
<template #item-fields="{item: item2, index: index2}">
Works perfectly as expected!

laravel 9 vue 3 edit multiple checked checkbox value

when editing a checkbox array, I cannot select (checked) that are in the database
how to select those that exist in the checkbox database (checked) ?
please, help
Edit.vue
<template>
<div>
<admin-layout>
<template #header>
<div class="row">
<div class="col-md-12">
<h4>Емделуші</h4>
</div>
</div>
</template>
<div class="row">
<div class="col-md-12 mb-3">
<div class="card">
<div class="card-header">
<span><i class="bi bi-table me-2"></i></span> Емделушіні өзгерту
<div class="card-header-pills float-end">
<inertia-link :href="route('admin.schedules.index')" class="btn btn-primary text-white text-uppercase" style="letter-spacing: 0.1em;">
Кері қайту
</inertia-link>
</div>
</div>
<form #submit.prevent="updateSchedule">
<div class="card-body">
<div class="mb-3">
<label for="name" class="form-label">Аты-жөні</label>
<input type="text" class="form-control" id="name" v-model="form.name" :class="{'is-invalid' : form.errors.name }" autofocus="autofocus" autocomplete="off">
</div>
<div class="invalid-feedback mb-3" :class="{ 'd-block' : form.errors.name }">
{{ form.errors.name }}
</div>
<div class="mb-3">
<label class="form-label" for="email">Email</label>
<input type="text" class="form-control" id="email" v-model="form.email" :class="{'is-invalid' : form.errors.email }" autofocus="autofocus" autocomplete="off">
</div>
<div class="invalid-feedback mb-3" :class="{ 'd-block' : form.errors.email }">
{{ form.errors.email }}
</div>
<div class="mb-3">
<label class="form-label" for="phone">Телефон</label>
<input type="text" class="form-control" id="phone" v-model="form.phone" :class="{'is-invalid' : form.errors.phone }" autofocus="autofocus" autocomplete="off">
</div>
<div class="invalid-feedback mb-3" :class="{ 'd-block' : form.errors.phone }">
{{ form.errors.phone }}
</div>
<div class="form-check form-check-inline" v-for="(time, index) in times" :key="index">
<input class="form-check-input" type="checkbox" id="inlineCheckbox1" :value="time.time" v-model="form.times" :class="{'is-invalid' : form.errors.times }">
<label class="form-check-label" for="inlineCheckbox1">{{ time.time }}</label>
</div>
<div class="invalid-feedback mb-3" :class="{ 'd-block' : form.errors.times }">
{{ form.errors.times }}
</div>
</div>
<div class="card-footer clearfix">
<div class="float-end">
<jet-button class="ms-4 btn-primary text-white" :class="{ 'text-white-50': form.processing }" :disabled="form.processing">
<div v-show="form.processing" class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Өзгертілуде...</span>
</div>
Өзгерту
</jet-button>
</div>
</div>
</form>
</div>
</div>
</div>
</admin-layout>
</div>
</template>
<script>
import AdminLayout from '#/Layouts/AdminLayout';
import JetButton from '#/Jetstream/Button.vue'
import { useForm } from '#inertiajs/inertia-vue3';
import InertiaLink from "#inertiajs/inertia-vue3/src/link";
export default {
name: "Edit",
components: {
AdminLayout,
JetButton,
InertiaLink,
},
props: {
appointment: {},
times: {},
},
setup (props) {
const form = useForm({
name: props.appointment.name,
email: props.appointment.email,
phone: props.appointment.phone,
times: props.times,
});
return {
form,
}
},
methods: {
},
}
}
</script>
when editing a checkbox array, I cannot select (checked) that are in the database how to select those that exist in the checkbox database (checked) ? please, help

Nuxt component variable not updated

I wrote the following code for my navbar:
<template>
<div>
<div v-if="!$auth.loggedIn" data-tip="Login" class="tooltip tooltip-bottom">
<nuxt-link to="/login" class="btn btn-square btn-ghost">
<solid-login-icon class="w-6 h-6" />
</nuxt-link>
</div>
<div v-else class="flex items-stretch">
<a class="pr-2 btn btn-ghost rounded-btn"> Welcome, {{ this.$auth.user.username }}! 👋 </a>
<div data-tip="Logout" class="tooltip tooltip-bottom">
<button class="pr-2 btn btn-square btn-ghost" #click="logout()">
<solid-login-icon class="w-6 h-6" />
</button>
</div>
</div>
</div>
</template>
Which is include in the following login page:
<template>
<div class="h-screen shadow bg-base-200 drawer drawer-mobile">
<input id="my-drawer-2" type="checkbox" class="drawer-toggle" />
<div class="drawer-content">
<NavBar />
<div class="grid grid-cols-1 p-5 md:grid-cols-6">
<div class="col-span-1 md:col-span-2 md:col-start-3">
<div class="bg-white shadow card">
<div class="card-body">
<h2 class="text-left card-title">Login</h2>
<form #submit.prevent="userLogin">
<div class="form-control">
<label class="label">
<span class="label-text">Username</span>
</label>
<input
v-model="login.username"
type="text"
placeholder="Username"
class="input input-bordered"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Username</span>
</label>
<input
v-model="login.password"
type="password"
placeholder="Password"
class="input input-bordered"
/>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-primary btn-md">Login</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<SideBar />
</div>
</template>
<script>
export default {
data() {
return {
login: {
username: '',
password: '',
},
}
},
methods: {
async userLogin() {
try {
const response = await this.$auth.loginWith('local', { data: this.login })
} catch (err) {}
},
},
}
</script>
Upon loggin in with correct credentials the page keeps dispalying the "Login"-icon instead of the Welcome message.
I've tried to replace !$auth.loggedIn with loggedIn (coming from the data function) and with !this.$auth.loggedIn" but both don't seem to solve the issue

VueJS reusable component not updated data for second component

I am trying to create one component in VueJS and need to reuse that component.
below is code for me.
Vue.component("radio" , {
props: ['selectGender'],
data: function() {
return{
selected: 1
}
},
template : `
<div class="modal fade" :id="compId" style="background: rgb(0, 0, 0, 0.8);">
<div class="modal-dialog default-font" style="top: 30%;">
<div class="modal-content center">
<div class="modal-header base-bg center">
<div class="center">
<span class="validationHeadSpan">Select Gender</span><br/>
</div>
<button type="button" class="close modal-close" data-dismiss="modal" style="font-size:3rem">×</button>
</div>
<div class="modal-body t-left" id="printArea">
<div class="container">
<div class="row" style="padding: 0rem 1rem;">
<div class="col">
<div class="custom-control custom-radio" style="margin: 1rem 0rem;">
<input class="custom-control-input" type="radio" id="rdbMale" value="0" checked v-model="selected"/>
<label class="custom-control-label" for="rdbMale">
<div class="addr_header_1" style="margin-top: 0.8rem;">Male</div>
</label>
</div>
</div>
<div class="col">
<div class="custom-control custom-radio" style="margin: 1rem 0rem;">
<input class="custom-control-input" type="radio" id="rdbFemale" value="0" checked v-model="selected"/>
<label class="custom-control-label" for="rdbFemale">
<div class="addr_header_1" style="margin-top: 0.8rem;">Female</div>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer center">
<button type="button" class="button" data-dismiss="modal" v-on:click="selectGender(selected)"><span class="validationButtonContent">Select</span></button>
</div>
</div>
</div>
</div>
`,
mounted : function(){
},
methods : {
}
});
Here is my another component from where i am opening reusable component.
Vue.component("Component1" , {
data: function() {
return{
selected: 1
}
},
template : `
<div>
<radio ref="returnOpenFirst" :selectGender="selectGenderFirst"></radio>
<radio ref="returnOpenSecond" :selectGender="selectGenderSecond"></radio>
</div>
<div class="btn btn-primary formButton" v-on:click="openFirst">
<span>Open First</span>
</div>
<div class="btn btn-primary formButton" v-on:click="openSecond">
<span>Open First</span>
</div>
`,
mounted : function(){
},
methods : {
openFirst:function(){
jQuery(self.$refs.returnOpenFirst.$el).modal({
'backdrop' : false
}).modal('show');
},
openSecond:function(){
jQuery(self.$refs.returnOpenSecond.$el).modal({
'backdrop' : false
}).modal('show');
},
selectGenderFirst:function(gender){
console.log("First Gender", gender);
},
selectGenderSecond:function(gender){
console.log("Second Gender", gender);
},
}
});
While opening second component, data property is updated of first component only not for second component.
Any help is highly appreciated.
Thanks in advance.
Here i found solution.
Vue.component("radio" , {
props: ['selectGender','type'],
data: function() {
return{
selected: 1
}
},
template : `
<div class="modal fade" :id="compId" style="background: rgb(0, 0, 0, 0.8);">
<div class="modal-dialog default-font" style="top: 30%;">
<div class="modal-content center">
<div class="modal-header base-bg center">
<div class="center">
<span class="validationHeadSpan">Select Gender</span><br/>
</div>
<button type="button" class="close modal-close" data-dismiss="modal" style="font-size:3rem">×</button>
</div>
<div class="modal-body t-left" id="printArea">
<div class="container">
<div class="row" style="padding: 0rem 1rem;">
<div class="col">
<div class="custom-control custom-radio" style="margin: 1rem 0rem;">
<input class="custom-control-input" type="radio" :id="'rdbMale'+type" value="0" checked v-model="selected"/>
<label class="custom-control-label" :for="'rdbMale'+type">
<div class="addr_header_1" style="margin-top: 0.8rem;">Male</div>
</label>
</div>
</div>
<div class="col">
<div class="custom-control custom-radio" style="margin: 1rem 0rem;">
<input class="custom-control-input" type="radio" :id="'rdbFemale'+type" value="0" checked v-model="selected"/>
<label class="custom-control-label" :for="'rdbFemale'+type">
<div class="addr_header_1" style="margin-top: 0.8rem;">Female</div>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer center">
<button type="button" class="button" data-dismiss="modal" v-on:click="selectGender(selected)"><span class="validationButtonContent">Select</span></button>
</div>
</div>
</div>
</div>
`,
mounted : function(){
},
methods : {
}
});
Issue with same id created by VueJs, we need to pass one dynamic property and with the use of that we need to create id for radio.
Thank you guys.

Is there a way to get the data from multiple selects as array?

I'm currently stuck on a task on VueJS. Basically I'm trying to create an array from multiple select inside a form to get all the data as an array or Object to process it further. I just searched but got no results or an idea on how to solve my issue.
Parent Component
<template>
<div id="product" class="mt-12">
<div class="flex flex-wrap mb-4">
<div class="w-1/2 pr-12">
<img src="https://placehold.it/800" alt="">
</div>
<div class="w-1/2">
<div class="flex justify-between content-center h-12 mb-4">
<div class="text-gray-700 py-2">
<h2 v-if="product.brand" class="leading-none font-bold text-sm">{{ product.brand }}</h2>
<h1 class="text-xl leading-snug">{{ product.name }}</h1>
</div>
<div class="text-gray-700 py-2">
<p class="text-xl font-bold leading-none">{{ product.price }}</p>
<p class="text-xs text-right leading-none">Inkl. 19% MwSt.</p>
</div>
</div>
<div class="divider bg-gray-400 mt-4 mb-4" />
<div id="variations">
<form action="">
<ProductVariation v-for="(variations, type) in product.variations" :type="type" :variations="variations" :key="type" v-model="form.variation"/>
<div class="flex w-full">
<button class="w-full leading-none bg-blue-500 hover:bg-blue-600 text-white py-2 rounded" type="submit">Add to cart</button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
import ProductVariation from '#/components/products/ProductVariation'
export default {
data () {
return {
product: null,
form: {
variation: null
}
}
},
components: {
ProductVariation
},
async asyncData({ params, app }) {
let response = await app.$axios.$get(`products/${params.slug}`)
return {
product: response.data
}
}
}
</script>
ProductVariation Component
<template>
<div class="flex mb-4">
<div class="w-full">
<label class="block text-gray-700 w-full text-sm font-bold">{{ type }}</label>
<select class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight" :name="type">
<option value="0">Please select</option>
<option v-for="variation in variations" :key="variation.id" :value="variation.id">{{ variation.name }} <span>( +{{ variation.price }} )</span></option>
</select>
</div>
</div>
</template>
<script>
export default {
props: {
type: {
required: true,
type: String
},
variations: {
required: true,
type: Array
}
},
}
</script>