Vuetify checkboxes array checks all boxes when list changes - vue.js

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"
}
]
}
}
});

Related

How can I show a different value apart from the dropdown options in v-select in vue 2 js

Well the scenario is when the selectedFruits is having some element then I need to show "Fruits Selected" and If there is no fruit selected it should display "Select Fruits" in the select. Below is my template and the data I am using. So basically it won't show the fruits selected but display the messages mentioned above in the v-select dropdown. I am new to the vue js, so wondering whether this is possible or not? or is there any alternate way to achieve the same scenario.
<template>
<div>
<v-select
v-model="selectedFruits"
:items="fruits"
label="name"
multiple
/>
</div>
</template>
<script>
export default {
data() {
return {
fruits: [
{ name: 'Apple' },
{ name: 'Mango' },
{ name: 'Banana' },
{ name: 'Berries' },
{ name: 'Muskmelon' }
],
selectedFruits: []
}
}
}
</script>
Well, I found that there is an option called selection which we can use with v-slot through which I am able to achieve the desired result. Below is the changes I made in my code:
<template>
<div>
<v-select
v-model="selectedFruits"
:items="fruits"
label="Select Fruits"
multiple
>
<template v-slot:selection="{ item, index }">
<span v-if="index === 0">{{
item && "Fruits Selected"
}}</span>
</template>
</v-select>
</div>
</template>
<script>
export default {
data() {
return {
fruits: [
{ name: 'Apple' },
{ name: 'Mango' },
{ name: 'Banana' },
{ name: 'Berries' },
{ name: 'Muskmelon' }
],
selectedFruits: []
}
}
}
</script>

Sort Vue data table by selection

Is there a way to sort a v-data-table by the selection column?
I would like to multisort my v-data-table. First by selection, so the selected ones are always on top and second by name. So far I got this:
<template>
<v-app>
<div id="app">
<v-data-table
:headers="tableHeaders"
:items="selectables"
:show-select="true"
:sort-by="['isSelected', 'name']"
multi-sort
v-model="output"
return-object
>
</v-data-table>
</div>
</v-app>
</template>
<script>
export default {
data() {
return {
output: {},
tableHeaders: [
{
text: "name",
value: "name",
},
],
selectables: [
{
name: "toni",
},
{
name: "hans",
},
{
name: "fritz",
},
],
};
},
};
</script>

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)

How can I sort a v-data-table by default?

I can't seem to get default sorting to work. All I can see for the argument custom-sort in the documentation is that it's a "Function used to sort items", but I don't know in what way. I can imagine many. Is it called for an initial sort? It seems to return a list of items, but when I try to create one, I get an error saying this.customSort is not a function.
<template>
<v-data-table
:headers="headers"
:items="reports"
hide-default-footer>
<template v-slot:item.requested="{ item }">
{{ datetimeToDistance(item.requested) }}
</template>
</v-data-table>
</template>
<script>
export default {
name: 'Reports',
data () {
return {
customSort: (items,index,isDesc) => console.log("never called"),
reports: [{name:"a",requested:"2020-01-01T00:00:00Z"}.{name:"b",requested:"2020-02-02T00:00:00"}],
}
},
computed: {
headers () {
return [
{text: "Name", value: "name"},
{text: "Report Type", value: "report_type"},
{text: "Requested", value: "requested", sort: (a,b) => a.localeCompare(b) },
];
},
}
}
</script>
My sorting works if you click on the links. All I really want here is to say: "When the page first loads, sort by 'requested' as though the user clicked that one initially. Then let them change the ordering."
Note: The datetimeToDistance is just a function which calls a library and isn't too important. It's just that the output of that column is not directly in the objects.
Use the sort-by and the sort-desc properties with the .sync option, and set the desired values in data.
<template>
<div>
<v-data-table
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
></v-data-table>
</div>
</template>
<script>
export default {
data () {
return {
sortBy: 'fat',
sortDesc: false,
},
}
</script>
https://vuetifyjs.com/en/components/data-tables/#external-sorting
I usually sort v-datatable in the pagination directive as below:
<template>
<v-data-table :headers="headers" :items="reports" :pagination.sync="pagination" hide-default-footer>
<template v-slot:item.requested="{ item }">
{{ datetimeToDistance(item.requested) }}
</template>
</v-data-table>
</template>
<script>
export default {
name: 'Reports',
data() {
return {
customSort: (items, index, isDesc) => console.log("never called"),
reports: [{ name: "a", requested: "2020-01-01T00:00:00Z" }.{ name: "b", requested: "2020-02-02T00:00:00" }],
pagination: { sortBy: 'requested', descending: true, rowsPerPage: 10 }
}
},
computed: {
headers() {
return [
{ text: "Name", value: "name" },
{ text: "Report Type", value: "report_type" },
{ text: "Requested", value: "requested", sort: (a, b) => a.localeCompare(b) },
];
},
}
}
</script>

Vue js v-model different checkboxes

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);
}
}