Remove Chips from Vuetify Multi Select in v-for loop - vue.js

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)

Related

Move row from one dynamic table to another dynamic table in vuetify

I haven't found a question that addresses this problem using vuetify/vue.
I have a dynamic table, and on that page is an add item button. Clicking button pops up a dialog with anther dynamic table. I want to be able to click an add icon for each specific table row. Clicking the icon would move it to the original dynamic table.
I tried using something similar to the delete row function. I ended up getting two empty rows added, with the error " Invalid prop: type check failed for prop "item". Expected Object, got Number with value 0"
Here is what I have that creates that error.
HTML
<v-data-table
:headers="headers"
:items="agents"
sort-by="calories"
class="elevation-1"
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="800px">
<template v-slot:activator="{ on }">
<v-btn color="primary" dark class="mb-2" v-on="on">Add Agent</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">New Agent</span>
</v-card-title>
<v-data-table :headers="headers2" :items="newAgents">
<template v-slot:item.action="{ item }">
<v-icon small #click="addItem(item)">
mdi-plus-circle-outline
</v-icon>
</template>
</v-data-table>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.action="{ item }">
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
import axios from "axios";
export default {
data: () => ({
dialog: false,
isLoading: true,
headers: [
{ text: "Host IP Address", value: "host_ip" },
{ text: "Hostname", value: "host_name" },
{ text: "Agent Version", value: "agent_version" },
{ text: "Agent Install Location", value: "install_location" },
{ text: "Agent Status", value: "agent_status" },
{ text: "Actions", value: "action", sortable: false }
],
headers2: [
{ text: "Host IP Address", value: "host_ip" },
{ text: "Hostname", value: "host_name" },
{ text: "Agent Version", value: "agent_version" },
{ text: "Agent Install Location", value: "install_location" },
{ text: "Agent Status", value: "agent_status" },
{ text: "Add", value: "action", sortable: false }
],
agents: [],
newAgents: []
}),
watch: {
dialog(val) {
val || this.close();
}
},
created() {
this.initialize();
axios
.get("https://my.api.mockaroo.com/add_new_agent.json?key=88c9bdc0")
.then(res => (this.newAgents = res.data))
.then(res => {
console.log(res);
})
.catch(err => console.log(err));
},
methods: {
initialize() {
this.agents = [
{
host_ip: "Frozen Yogurt",
host_name: 159,
agent_version: 6.0,
install_location: 24,
agent_status: 4.0
},
{
host_ip: "Ice cream sandwich",
host_name: 237,
agent_version: 9.0,
install_location: 37,
agent_status: 4.3
}
];
},
changeColor() {
this.isLoading = !this.isLoading;
},
deleteItem(item) {
const index = this.agents.indexOf(item);
confirm("Are you sure you want to delete this item?") &&
this.agents.splice(index, 1);
},
addItem(item) {
const index = this.newAgents.indexOf(item);
confirm("Are you sure you want to add this item?") &&
this.agents.push(index, 1);
},
close() {
this.dialog = false;
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
}, 300);
},
save() {
if (this.editedIndex > -1) {
Object.assign(this.agents[this.editedIndex], this.editedItem);
} else {
this.agents.push(this.editedItem);
}
this.close();
}
}
}
I need to add a function to pull the id from the row. then use that id to push the row into another array.
added:
addItem(item) {
this.newAgentId(item.id);
console.log(item);
confirm("Are you sure you want to add this item?") &&
this.agents.push(item);
},
newAgentId(keyID) {
if (this.selectedRows.includes(keyID)) {
this.selectedRows = this.selectedRows.filter(
selectedKeyID => selectedKeyID !== keyID
);
} else {
this.selectedRows.push(keyID);
}
}

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

Show nested list using seperate component

I have my main page file called Scan.vue where I display a list with object. I don't know how many child objects a parent object has so I want to call another component called ScanList to display that info.
Scan:
<template>
<div>
<v-layout class="scanContainer">
<div class="scanSearch">
<input
type="text"
v-model="search"
placeholder="Rechercher une localisation :"
/>
</div>
<v-list>
<v-list-group
v-for="item in items"
v-bind:key="item"
v-model="item.active"
>
<v-list-tile slot="activator" #click="">
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
<ScanList></ScanList>
</v-list-group>
</v-list>
</v-layout>
</div>
</template>
<script>
import ScanList from "#/components/scan";
export default {
components: ScanList,
data: () => ({
items: [
{
id: 1,
title: "Location 1",
subItem: [
{
id: 1,
title: "Row 1",
qrCode: [
{ id: 1, title: "QR Code"}
]
},
{ id: 2, title: "Row 2" },
{ id: 3, title: "Row 3" },
{ id: 4, title: "Row 4" }
]
},
{
id: 2,
title: "Location 2",
subItem: [
{ id: 1, title: "Row 1" },
{
id: 2,
title: "Row 2",
qrCode: [
{ id: 2, title: "QR Code" }
]
},
{ id: 3, title: "Row 3" },
{ id: 4, title: "Row 4"}
]
}
]
}),
props: ["subItems"],
name: "Scan",
methods: {
showSelected(subItems) {
this.subItems = subItems;
this.items = true;
}
}
};
</script>
Here is the component I created called ScanList:
<template>
<v-list>
<v-list-group
v-for="subItem in item.subItem"
v-bind:key="subItem"
v-model="subItem.active"
>
<v-list-tile slot="activator" #click="">
<v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
</v-list-tile>
<v-list
v-for="qrCode in subItem.qrCode"
v-bind:key="qrCode"
v-model="qrCode.active"
>
<v-list-tile>
<v-list-tile-title>{{ qrCode.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-list-group>
</v-list>
</template>
<script>
export default {
name: "ScanList"
};
</script>
Nothing is showing up and I'm not sure why. Thanks for taking a look.

bootstrap-vue datatable show row details issue

trying to achieve the same result as https://bootstrap-vue.js.org/docs/components/table#row-details-support
My code:
<b-table ref="propertydata" striped hover
:items="datatable.items"
:fields="datatable.fields"
:current-page="datatable.currentPage"
:per-page="datatable.perPage"
:filter="datatable.filter"
:sort-by.sync="datatable.sortBy"
:sort-desc.sync="datatable.sortDesc"
:sort-direction="datatable.sortDirection"
#filtered="onFiltered">
<template slot="action" slot-scope="row">
<!-- we use #click.stop here to prevent emitting of a 'row-clicked' event -->
<b-button size="sm" #click.stop="row.toggleDetails" class="mr-2">
{{ row.detailsShowing ? 'Hide' : 'Show'}} Details
</b-button>
</template>
<template slot="row-details" slot-scope="row">
<b-card>
<ul>
<li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value}}</li>
</ul>
</b-card>
</template>
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination :total-rows="datatable.totalRows" :per-page="datatable.perPage" v-model="datatable.currentPage" class="my-0" />
</b-col>
</b-row>
/* Datatable related */
datatable: {
fields: [
{
key: 'account_id',
sortable: true
},
{
key: 'account_name',
sortable: true
},
{
key: 'property_count',
sortable: true,
},
{
key: 'commission',
sortable: true,
variant: 'success'
},
{
key: 'action',
}
],
items: [],
currentPage: 1,
perPage: 20,
totalRows: 0,
pageOptions: [ 20, 100, 300, 500, 1000, 2000, 3000 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null
}
computed: {
sortOptions () {
// Create an options list from our fields
return this.datatable.fields
.filter(f => f.sortable)
.map(f => { return { text: f.label, value: f.key } })
}
}, // END COMPUTED
methods: {
onFiltered (filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.datatable.totalRows = filteredItems.length
this.datatable.currentPage = 1
}
}, // END METHODS
The result is:
As you can see, the show details is isolated to a single column instead of the full row.
What I want to achieve is for this show details section to be the full row width and if I could click the button to go to a method to call additional data to be displayed here as oppose to the native bootstrap-vue functionality.

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