Vue js v-model different checkboxes - vue.js

I'm a bit new with Vue Js. I'm trying to obtain the boolean value of each checkbox from a Vue component, but when I check one, the rest ones are checked as well, so I can check just one. I've try it with computed but no results.
<v-card>
<v-layout row wrap class="text-xs-center" v-for="ingredient in ingredients" :key="ingredient.id">
<v-layout column>
<v-flex xs6>
<v-checkbox color="light-blue lighten-2" v-bind:label="`${ingredient.name}`" v-model="checked" light></v-checkbox>
</v-flex>
</v-layout>
<v-layout column>
<v-flex xs6>
<v-subheader>{{ingredient.price}} €</v-subheader>
</v-flex>
</v-layout>
</v-layout>
</v-card>
export default {
data: () => ({
checked: [],
checked1: '',
ingredients: [{
id: 1,
name: "tomato",
price: 2
}, {
id: 2,
name: "Cheese",
price: 2.0
}, {
id: 3,
name: "Frankfurt",
price: 2.25
}, {
id: 4,
name: "Mushrooms",
price: 1.6
}, {
id: 5,
name: "Pepper",
price: 2.5
}, {
id: 1,
name: "Ham",
price: 2.75
}],
}),
computed: {
checked() {
return this.checked
}
}
}

Try setting a checked value on each ingredient item:
ingredients: [{
id: 1,
name: "tomato",
price: 2,
checked: false
}]
And then you can set the value on the checkbox in the for-loop like this:
<v-checkbox v-model="ingredient.checked"></v-checkbox>

Simply bind :id and :value to an array
<div v-for="item, i in items>
<input type="checkbox" :id="i" :value="i" v-model="checked" />
</div>
export default {
data() {
return: {
checked: [],
items: []
};
},
created() {
axios.get('my-data').then(resp => this.items = resp.data.items);
}
}

Related

Remove Chips from Vuetify Multi Select in v-for loop

I'm using vuetify v.2 and creating some multi select via v-for.
everything is ok except removing chips. When I try to remove form any select, it removes from the last one. It got something to do with index of removed item as defined in remove method. Looks like all items get index of the last select.
Here is my code:
<template>
<v-row class="ma-0">
<v-col cols="12" sm="12" md="6" class="px-1" v-for="attribute in allAttr" :key="attribute.id">
<v-select
v-model="attrArr"
:items="attribute.items"
item-text="title"
item-value="id"
chips
:label="attribute.title"
multiple
outlined
#change="changed()"
>
<template v-slot:selection="{ attrs, item, select, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close
#click="select"
#click:close="remove(item)"
>
<strong>{{ item.title }}</strong>
</v-chip>
</template>
</v-select>
</v-col>
{{attrArr}}
</v-row>
</template>
<script>
export default {
data(){
return{
attrArr:[],
allAttr: null
}
},
async fetch(){
// fetch all attributes
//this.allAttr = await this.axiosGet(`attributes/0/1`)
this.allAttr = [
{
id: 1,
title: "color",
items:[
{
id: 11,
title: "blue"
},
{
id: 12,
title: "green"
},
{
id: 13,
title: "black"
}
]
},
{
id: 2,
title: "size",
items:[
{
id: 21,
title: "L"
},
{
id: 22,
title: "S"
}
]
},
{
id: 3,
title: "Material",
items:[
{
id: 31,
title: "X01"
},
{
id: 32,
title: "X02"
},
{
id: 33,
title: "X03"
},
{
id: 34,
title: "X04"
}
]
}
]
},
methods: {
remove (item) {
this.attrArr.splice(this.attrArr.indexOf(item), 1)
this.attrArr = [...this.attrArr]
},
changed(){
this.$emit('changed', this.attrArr)
}
},
}
</script>
You can just pass in the id instead of the entire item object...
<v-chip
v-bind="attrs"
:input-value="selected"
close
#click="select"
#click:close="remove(item.id)">
<strong>{{ item.title }}</strong>
</v-chip>
...
remove (id) {
let idx = this.attrArr.indexOf(id)
this.attrArr.splice(idx, 1)
this.attrArr = [...this.attrArr]
},
Demo: https://codeply.com/p/Cb5TYCS6Bt
in your remove method you're looking for the index of an object within attrArr which consists of ids only. Try this instead:
this.attrArr.splice(this.attrArr.indexOf(item.id), 1)

Change background of v-data-table row on event from child component

I have an expanding data table in my parent component and a child component inside the expanded row with a button. I would like to change the background color of the associated row when I click the button inside the child component. I'm not sure how to target the row to add the css class on event.
ScanGrid(parent):
<template>
<v-flex v-if="items.length === 0">
<ScanAdd #selectBatch="showScan" />
</v-flex>
<v-card v-else class="ma-5">
<v-card-text>
<v-layout align-center>
<v-data-table
:headers="headers"
:items="items"
item-key="StorageName"
show-expand
single-expand
:expanded="expanded"
hide-default-footer
#click:row="clickedRow"
>
<template
#isDeleted="deleteRow"
v-if="groupBy === 'barCode'"
v-slot:expanded-item="{ item }"
>
<td :colspan="12">
<ScanGridCode :item="item" />
</td>
</template>
<template v-else v-slot:expanded-item="{ item }">
<td :colspan="12">
<ScanGridDef :item="item" />
</td>
</template>
</v-data-table>
</v-layout>
</v-card-text>
</v-card>
</template>
<script>
import { API } from "#/api";
import ScanAdd from "./ScanAdd";
import ScanGridCode from "./ScanGridCode";
import ScanGridDef from "./ScanGridDef";
export default {
name: "ScanGrid",
props: {
items: {
type: Array,
required: true
}
},
components: {
ScanGridCode,
ScanGridDef,
ScanAdd
},
methods: {
deleteRow(value) {
this.isDeleted = value;
},
showScan(value) {
this.selectedId = value;
this.addScanBatch(value);
this.$emit("processingBatch", true);
this.processingBatch = true;
},
async addScanBatch(Id) {
const selectedItems = await API.getPhysicalInventoryBatch(Id);
if (selectedItems.data.Id === this.selectedId) {
this.items = selectedItems.data.Locations;
}
},
clickedRow(value) {
if (
this.expanded.length &&
this.expanded[0].StorageName == value.StorageName
) {
this.expanded = [];
} else {
this.expanded = [];
this.expanded.push(value);
}
}
},
data: () => ({
isDeleted: false,
groupBy: "barCode",
expanded: [],
items: [],
toDelete: "",
totalResults: 0,
loading: true,
headers: [
{
text: "Localisation",
sortable: true,
value: "StorageName",
class: "large-column font-weight"
},
{
text: "Paquets scannés",
sortable: true,
value: "ScannedProduct",
class: "large-column font-weight"
},
{
text: "Paquets entrants",
sortable: true,
value: "Incoming",
class: "large-column font-weight"
},
{
text: "Paquets sortants",
sortable: true,
value: "Outgoing",
class: "large-column font-weight"
},
{
text: "Paquets inconnus",
sortable: true,
value: "Unknown",
class: "large-column font-weight"
}
]
})
};
</script>
ScanGridCode(child):
<template>
<div class="codeContainer">
<div class="cancelLocation">
<v-flex class="justify-center">
<v-btn class="ma-5" large color="lowerCase" tile #click="deleteLocation"
>Annuler le dépôt de cette localisation</v-btn
>
</v-flex>
</div>
</div>
</template>
<script>
export default {
name: "ScanGridCode",
props: {
item: {
type: Object,
required: true
}
},
methods: {
deleteLocation() {
this.item.IsDeleted = true;
this.$emit("IsDeleted", true);
}
},
data: () => ({
IsDeleted: false,
groupBy: 0,
headersGroupCode: [
{
text: "Code barre",
sortable: true,
value: "SerialNumber",
class: "large-column font-weight-light"
},
{
text: "De",
sortable: true,
value: "FromLocation",
class: "large-column font-weight-light"
},
{
text: "Vers",
sortable: true,
value: "ToLocation",
class: "large-column font-weight-light"
}
]
})
};
</script>
I use Vuetify 2.1.7 and Vue 2.6.10. When I click on the button I call deleteLocation function. I assume I need to $emit a value to my parent but after that I don't know how to target the tr to change its style.
Since you're using Vuex, I would suggest using some variable such as store.state.selectedRow to keep track of whether or not a row has been selected (or in cases where there are more than one row, which row has been selected). Then you can have a computed property myProperty = this.$store.state.selectedRow in your Vue component which will automatically reflect the single source of truth, and your conditional class can be bound to this myProperty. This means you don't need to worry about emitting on events.
The approach to emitting the event is what should be done. So I am assuming you will emit from deleteLocation function.
Since you need a custom styling on rows you need to add the items slot and add your logic there
<template v-slot:item="{ item, select}">
<tr :class="key === coloredRow ? 'custom-highlight-row' : ''">
<td :colspan="12">
<ScanGridCode #changeColor="changeColor(key)" :item="item" />
</td>
//add this method to your script element
changeColor(idx) {
this.coloredRow = idx;
}

Vuetify checkboxes array checks all boxes when list changes

I'm pretty new to both vue and vuetify so there might be a few horrible lines in my code, but I'm really struggling with this one and a bit of help would be nice.
I have an array of checkboxes generated with a v-for loop on an "items" array. This array of checkboxes is attached to a model array just like this example from the vuetify documentation.
It looks like the code below.
The problem is : if I change the items array, even when the model array is still empty, all checkboxes end up checked.
Here is my template :
<div id="app">
<v-app>
<v-content>
<v-container>
<div>
<v-list>
<v-list-item
v-for="item in items" :key="item.id"
>
<v-checkbox
v-model="model" :label="item.name"
:value="item"
:value-comparator="comparator"
></v-checkbox>
</v-list-item>
</v-list>
<v-btn #click="updateItems">Change elements</v-btn>
</div>
</v-container>
</v-content>
</v-app>
</div>
and the script
new Vue({
el: "#app",
vuetify: new Vuetify(),
data() {
return {
model: [],
items: [
{
id: 1,
name: "Item1"
},
{
id: 2,
name: "Item2"
},
{
id: 3,
name: "Item3"
},
{
id: 4,
name: "Item4"
}
]
};
},
methods: {
comparator(a, b) {
return a.id == b.id;
},
updateItems() {
this.items = [
{
id: 1,
name: "Element1"
},
{
id: 2,
name: "Element2"
},
{
id: 3,
name: "Element3"
},
{
id: 4,
name: "Element4"
}
]
}
}
});
And a codepen is way easier to understand
I've been struggling with this issue for a while now, if you have any idea, that would be welcome. Thank you !
EDIT : I had made a mistake in my code. Fixed it. The question is still the same though.
There are few bugs in this code,
from the below checkbox
<v-checkbox
v-model="model" :label="item.name"
:value="item"
:value-comparator="comparator"
></v-checkbox>
:value-comparator is triggers when you click on checkbox, it tries to
match with all other value and returns true only for the selected id
"comparator" function is not available in your methods, replace "valueCompare" method with "comparator"
when you click on change elements, it resets items array but you are not reseting the model
working codepen : https://codepen.io/chansv/pen/rNNyBgQ
Added fixs and final code looks like this
<div id="app">
<v-app>
<v-content>
<v-container>
<div>
<v-list>
<v-list-item
v-for="item in items" :key="item.id"
>
<v-checkbox v-model="model" :label="item.name"
:value="item"
:value-comparator="comparator"
></v-checkbox>
</v-list-item>
</v-list>
<v-btn #click="updateItems">Change elements</v-btn>
</div>
</v-container>
</v-content>
</v-app>
</div>
// Looking for the v1.5 template?
// https://codepen.io/johnjleider/pen/GVoaNe
new Vue({
el: "#app",
vuetify: new Vuetify(),
data() {
return {
model: [],
items: [
{
id: 1,
name: "Item1"
},
{
id: 2,
name: "Item2"
},
{
id: 3,
name: "Item3"
},
{
id: 4,
name: "Item4"
}
]
};
},
methods: {
comparator(a, b) {
console.log(a, b);
return a.id == b.id;
},
updateItems() {
this.model = [];
this.items = [
{
id: 1,
name: "Element1"
},
{
id: 2,
name: "Element2"
},
{
id: 3,
name: "Element3"
},
{
id: 4,
name: "Element4"
}
]
}
}
});

Vuetify, v-for checkbox with search field. Strange behavior on the checkbox

I'm making a list of person to select with a search bar to find the person name. I'm using computed and filter to search the list. But there's strange behavior on my checklist. I don't know what happen. Please check the codepen link above. Try searching then delete the search.
https://codepen.io/rahmatfajar15/pen/OqPqRy?editors=1010
template:
<v-layout column fill-height>
<v-flex>
<v-text-field
v-model="pattern"
box
hide-details
label="Cari Peserta..."
prepend-inner-icon="search"
clear-icon="close"
clearable
/>
</v-flex>
<v-layout column>
<div
v-for="item in filteredPeserta"
>
<v-layout row class="text-xs-left">
<div class="xs2 justify-center align-center">
<v-checkbox
height="16"
v-model="tempPeserta"
:value="item.id"
/>
</div>
<v-layout xs10 column justify-center>
<pre class="body-2">{{ item.name }}</pre>
</v-layout>
</v-layout>
</div>
</v-layout>
</v-layout>
script:
new Vue({
el: '#app',
data: () => ({
pattern: '',
tempPeserta: [],
listPeserta: [
{
id: '1',
name: 'Agung'
},
{
id: '2',
name: 'Bucin'
},
{
id: '3',
name: 'Chandra'
},
{
id: '4',
name: 'Dedek'
},
{
id: '5',
name: 'Enok'
},
{
id: '6',
name: 'Fajar'
},
{
id: '7',
name: 'Galih'
},
{
id: '8',
name: 'Hayo'
},
{
id: '9',
name: 'Ilsa'
},
]
}),
computed: {
filteredPeserta () {
return this.listPeserta.filter(item => {
return item.name.toLowerCase().indexOf(this.pattern.toLowerCase()) > -1
})
}
}
})
You need to add key when using v-for because Vue will reuse list component (document)
<div
v-for="item in filteredPeserta"
:key="item.id"
>
....
</div>

I want to create dynamic fields using Vue.js and Vuetify

Here is my vuejs file, I want to create dynamic fields like text box, check box, radio button, drop down, text area etc. I have tried but I have got vue source code.
<template>
<v-container>
<v-layout>
<v-flex xs12 sm12>
<v-card>
<v-card-title primary-title>
<v-layout>
<v-flex xs12 sm6 md3 v-for="(item, i) in field_output" :key="i">
{{item.template}}
</v-flex>
</v-layout>
</v-card-title>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
Here I have create fields array which contains the all fields which I need. Through create_forms() function I have create forms fields.
<script>
export default {
data() {
return {
fields: [{
type: 'text',
text: 'CSP Address',
default_value: '',
meta_id: 'csp_address'
},
{
type: 'text',
text: 'CSP Name',
default_value: '',
meta_id: 'csp_name'
},
{
type: 'radio',
text: 'CSP Gender',
default_value_one: 'male',
default_value_two: 'female',
meta_id: 'csp_gender'
},
{
type: 'check_box',
text: 'CSP Agree',
default_value: false,
meta_id: 'csp_aggree'
}
],
field_output:null
}
},
created: function(){
this.create_forms()
},
methods:{
create_forms: function(){
var field_output = [];
this.fields.forEach(function (item, key) {
var input_field;
switch(item.type){
case 'text':
input_field = '<v-text-field type="text" v-model="input_value.'+item.meta_id+'" label="'+item.text+'" outline></v-text-field>';
break;
case 'radio':
input_field = '<v-radio-group v-model="input_value.'+item.meta_id+'"><v-radio :label="'+item.default_value_one+'" :value="'+item.default_value_one+'"></v-radio><v-radio :label="'+item.default_value_two+'" :value="'+item.default_value_two+'"></v-radio></v-radio-group>';
break;
case 'check_box':
input_field = ' <v-checkbox :label="'+item.text+'" v-model="input_value.'+item.meta_id+'"></v-checkbox>';
break;
case 'select':
break;
case 'textarea':
break;
}
field_output.push({id: key+1, template:input_field});
})
this.field_output = field_output;
console.log(this.field_output);
}
}
}
</script>
My result is :
I need text field, radio button, check box etc. Not vue code. Please help me
I would suggest using VueJS <component/> and load required form field:
This is a small working example, you can change it to your needs.
Template:
<template>
<v-flex>
<component :is="item.type" :label="item.text" v-for="(item, i) in fields" :key="i" v-model="values[item.meta_id]">
<component v-if="item.children && item.children.length > 0" :is="children.type" :value="children.value" :label="children.text" v-for="(children, j) in item.children" :key="j"/>
</component>
{{ JSON.stringify(values) }}
</v-flex>
</template>
Change your fields array to:
<script>
export default {
data(){
return {
values: {
csp_address: 'default value',
csp_name: 'default value',
csp_gender: 'male',
csp_aggree: true
},
fields: [
{
type: 'v-text-field',
text: 'CSP Address',
meta_id: 'csp_address'
},
{
type: 'v-text-field',
text: 'CSP Name',
meta_id: 'csp_name'
},
{
type: 'v-radio-group',
text: 'CSP Gender',
children: [
{
type: 'v-radio',
value: 'male',
text: 'Male',
},
{
type: 'v-radio',
value: 'female',
text: 'Female',
}
],
meta_id: 'csp_gender'
},
{
type: 'v-checkbox',
text: 'CSP Agree',
meta_id: 'csp_aggree'
}
]
}
}
}
</script>
What you are doing right now is printing out a string which vuejs won't recogize as html.
As shown in the docs here: https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML
you can use the v-html directive to print out raw html:
<div v-html="{{ item.template }}"></div>