Vuetify: Data table slot value to toggle another slot value - vue.js

I am using a Vuetify data table and I am using v-slot:item on two of the columns to insert switches.
<v-data-table
:headers="headers"
:items="records"
:search="search"
:items-per-page="5"
>
<template v-slot:item.monitor="{ item }">
<v-switch v-model="item.monitor" color="success"></v-switch>
</template>
<template v-slot:item.manage="{ item }">
<v-switch v-model="item.manage" color="success"></v-switch>
</template>
</v-data-table>
What I want to do is set the switches like an Item Group so that only one may be selected (on) at a time, but both can be false (off). So only one can have a true value.
I have tried using the Item Group component and got that to work, but I am not sure if it is even possible within the table the way I want since these two switches are in separate slots. I am under the impression that the v-item should be sibling components, so that makes me think that would't work for this situation.

The way I see it, there are two different approches to this problem. You can either:
Watch the records array to detect changes in either monitor or manage attributes of one of its objects.
Do not use v-model directive on the switches but provide a readonly value, then change it manually on click.
I prefer the second option. You can do it like so:
Template section
<div id="app">
<v-app id="inspire">
<v-data-table :headers="headers" :items="records">
<template v-slot:item.monitor="{ item }">
<v-switch
:input-value="item.monitor"
readonly
color="success"
#click.stop="setSwitchesForItem(item.id, 'monitor', !item.monitor)"
/>
</template>
<template v-slot:item.manage="{ item }">
<v-switch
:input-value="item.manage"
readonly
color="success"
#click.stop="setSwitchesForItem(item.id, 'manage', !item.manage)"
/>
</template>
</v-data-table>
</v-app>
</div>
A couple of things to note here:
v-model has been replaced by :input-value
readonly props has been set
the click event calls a custom method. It requires the .stop suffix because of a bug documented here
Script section
export default {
// ...
data() {
return {
headers: [
{ text: "Name", value: "name" },
{ text: "Monitor", value: "monitor" },
{ text: "Manage", value: "manage" }
],
records: [
{
id: 1,
name: "Item 1",
monitor: true,
manage: false
},
{
id: 2,
name: "Item 2",
monitor: false,
manage: true
},
{
id: 3,
name: "Item 3",
monitor: false,
manage: false
}
]
};
},
methods: {
setSwitchesForItem(itemId, attributeName, attributeValue) {
const record = this.records.find(r => r.id === itemId);
// In case the new value is true, we need to make sure the other one is set to false
if (attributeValue) {
record[attributeName === "monitor" ? "manage" : "monitor"] = false;
}
record[attributeName] = attributeValue;
}
}
}
Link to the Codepen.

Related

How to display Checkbox in DataTable body Vuetify

I have a Vue application which is built using Vuetify and I want to create a table where cell content is a checkbox v-checkbox but it renders html string instead of checkbox inside table body.
I use v-data-table as a main table.
For illustration:
My Code:
<v-data-table :headers="headers"
:items="menuaccess"
:search="search"
sort-by="alias"
class="elevation-3"
v-html="value"> <--- I tried add this but not working
<!-- other code -->
Data source snippet (I will get data from API later) :
headers: [
{ text: '', align: 'start', value: 'alias' },
{ text: 'ASSET_EDIT', value: '<v-checkbox class="mx-2" label="Example"></v-checkbox>' },
{ text: 'ASSET_VERIFICATION', value: '<v-checkbox class="mx-2" label="Example"></v-checkbox>' },
{ text: 'ASSET_VIEW', value: '<v-checkbox class="mx-2" label="Example"></v-checkbox>' },
{ text: 'CUSTOMER_COMPANY_EDIT', value: '<v-checkbox class="mx-2" label="Example"></v-checkbox>' },
],
You need to place code inside slot template.
<v-data-table :headers="headers"
:items="menuaccess"
:search="search"
sort-by="alias"
class="elevation-3">
<template v-slot:item.property_name="{ item }">
<v-simple-checkbox v-model="item.property_name" />
</template>
</v-data-table>
Where property_name is name of the property on your data object.
Read more about it in the documentation.

Vuetify data table in single-select mode, selecting one row selects all others

I am trying to select one row in the table and emit the selected item.
Selecting one selects all but only the first encountered object is saved to the model (as selected variable).
Do you have any ideas, what i'm doing wrong?
<template>
<v-data-table
:headers="headers"
:items="items"
:search="search"
:loading="loading"
v-model="selected"
single-select
show-select
:options="{itemsPerPage:5}"
#item-selected="itemSelected"
>
<template v-slot:top>
<v-toolbar flat>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-toolbar>
</template>
<template v-slot:item.name="{ item }">{{ item.name }}</template>
</v-data-table>
</template>
<script>
export default {
name: "variable-selector",
props: ["variables", "map", "index"],
data() {
return {
search: "",
selected: {},
loading: false,
items: [],
headers: [{ text: "Variable name", value: "name", sortable: true }]
};
},
methods: {
itemSelected(selection) {
if (selection.value) {
this.$emit("selected", selection.item); // it always emits var_2 object
} else {
this.$emit("selected", null);
}
},
updateItemsList(variables) {
this.items = Array.from(variables);
}
},
mounted() {
this.updateItemsList(this.variables);
},
watch: {
variables(newValue) {
this.loading = true;
this.updateItemsList(newValue);
this.loading = false;
}
}
};
</script>
Each object should be unique key value if u face error , you want manually tell each object is unique
just add
item-key="table_header_index"//or name
eg:
<v-data-table
:headers="headers"
:items="items"
show-select
item-key="table_header_index" <-------------------add this line
>
</v-data-table>
I had this issue and the realized my item-key="value" did not match any of the values in my header. Select one of your header values and it should work.
From the example in the docs I can see the following:
1.) selected should be an array, not an object
Selected holds all selected values. single-select property just determines if the length can be bigger than 1.
2.) if you use v-model you should not use #item-selected="itemSelected"
v-model is already 2 way binding. but you trigger an additional event and override the model (which should be an array) with an object or null
Solution
Make selected an array and remove #item-selected="itemSelected".
<template>
<v-data-table
:headers="headers"
:items="items"
:search="search"
:loading="loading"
v-model="selected"
single-select
show-select
:options="{itemsPerPage:5}"
>
<template v-slot:top>
<v-toolbar flat>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-toolbar>
</template>
<template v-slot:item.name="{ item }">{{ item.name }}</template>
</v-data-table>
</template>
<script>
export default {
name: "variable-selector",
props: ["variables", "map", "index"],
data() {
return {
search: "",
selected: [],
loading: false,
items: [],
headers: [{ text: "Variable name", value: "name", sortable: true }]
};
},
methods: {
updateItemsList(variables) {
this.items = Array.from(variables);
}
},
mounted() {
this.updateItemsList(this.variables);
},
watch: {
variables(newValue) {
this.loading = true;
this.updateItemsList(newValue);
this.loading = false;
}
}
};
</script>
your data from back should have primary key. if it does it your table understand it; and if your data doesn't have primary key should write item-key for table.
good luck

Vuejs, Dynamic b-table with editable fields and two-way databinding

I'm trying to generate a dynamic b-table with editable fields and with two-way databinding.
I would like to not have any hardcoded values. Now, I have:
<b-table striped hover :items="filtered">
<template v-slot:cell(issueDescription)="row">
<b-form-input v-model="row.item.issueDescription" />
</template>
<template v-slot:cell(endTime)="row">
<b-form-input v-model="row.item.endTime" />
</template>
<template v-slot:cell(startTime)="row">
<b-form-input v-model="row.item.startTime" />
</template>
</b-table>
Where:
filtered = [ { "issueDescription": "this is a description", "endTime": "2020-02-11T14:00:00.000Z",
"startTime": "2020-02-11T01:24:00.000Z" }]
If I generate template with a v-for, then I got editable fields in every column, but no binding at each field.
<b-table striped hover :items="filtered">
<template v-for="x in filtered" v-slot:cell()="row">
<b-form-input v-model="row.item.BIND_TO_SPECIFIC_TABLE_ROW_COL" />
</template>
</b-table>
How do I bind to the specific row,col??
I've made a fiddle: https://jsfiddle.net/gfhu1owt/
If you want to have ALL fields editable, you can use this syntax.
<template v-slot:cell()="{ item, field: { key } }">
<b-form-input v-model="item[key]" />
</template>
It's pretty similar to what you had. You just needed to use the key from the slot context object. (I've shorthanded it a bit, but it's the same as going row.field.key).
Also note that i don't use a v-for in the template, this is because v-slot:cell() is a fallback slot which is valid for all slots unless a specific one is defined. For example v-slot:cell(issueDescription) would overwrite v-slot:cell() for the issueDescription field.
While the above works, the problem might come one day when you have a field you DONT want to be editable, like maybe an id field in your object.
To solve this issue, I've defined my fields and passed them to the table. I've also added a editable property to the fields i want to be editable. (note this is not a standard thing in the field object, but something specific for you use-case).
I then created a computed property editableFields which returns all fields that have editable: true, and then use editableFields in my v-for inside b-table.
This way you can pick and choose which properties you want to be editable in your objects.
new Vue({
el: "#app",
computed: {
editableFields() {
return this.fields.filter(field => field.editable)
}
},
data: {
filtered: [
{
"id": "1",
"issueDescription": "this is a description",
"endTime": "2020-02-11T14:00:00.000Z",
"startTime": "2020-02-11T01:24:00.000Z"
},
{
"id": "2",
"issueDescription": "this is a description",
"endTime": "2020-02-11T14:00:00.000Z",
"startTime": "2020-02-11T01:24:00.000Z"
}
],
fields: [
{ key: 'id', label: 'ID' },
{ key: 'issueDescription', label: 'Issue Description', editable: true },
{ key: 'startTime', label: 'Start Time', editable: true },
{ key: 'endTime', label: 'End Time', editable: true }
]
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.4.1/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.4.1/dist/bootstrap-vue.min.js"></script>
<div id='app'>
<b-table :items="filtered" :fields="fields">
<template v-for="field in editableFields" v-slot:[`cell(${field.key})`]="{ item }">
<b-input v-model="item[field.key]"/>
</template>
</b-table>
</div>

Vuetify insert action button in data-table and get row data

Environment:
vue#^2.6.10:
vuetify#^2.1.0
I want to use v-data-table to show search results and add evaluate button in each row in the v-data-table.
Unfortunately I have two issues:
Evaluate buttons are not shown
I don't know how to get row data of pushed button
What do I need to change?
Template
<v-data-table
:headers="headers"
:items="search_result"
>
<template slot="items" slot-scope="row">
<td>{{row.item.no}}</td>
<td>{{row.item.result}}</td>
<td>
<v-btn class="mx-2" fab dark small color="pink">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</td>
</template>
</v-data-table>
Script
data () {
return {
headers: [
{ text: 'no', value: 'no' },
{ text: 'result', value: 'result' },
{ text: 'good', value: 'good'},
],
// in real case initial search_result = [], and methods: search function inject below data
search_result: [{no: 0, result: 'aaa'}, {no:2, result: 'bbb'],
}
},
slot name used to "replace the default rendering of a row" is item, not items
Add wrapping <tr> into slot template
Just add #click="onButtonClick(row.item) to v-btn and create method onButtonClick
<v-data-table :headers="headers" :items="search_result">
<template v-slot:item="row">
<tr>
<td>{{row.item.no}}</td>
<td>{{row.item.result}}</td>
<td>
<v-btn class="mx-2" fab dark small color="pink" #click="onButtonClick(row.item)">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</td>
</tr>
</template>
</v-data-table>
methods: {
onButtonClick(item) {
console.log('click on ' + item.no)
}
}
Note..
...solution above is replacing default row rendering with your own so expect some of the v-data-table features to not work (didn't try but I expect row selection, grouping, in place editing etc. will be broken). If that's problem for you, here is alternative solution:
Add one more column to your headers definition: { text: "", value: "controls", sortable: false }
Do not override item slot (row rendering). Override item.controls slot instead. Notice "controls" is the same as in column definition - we are overriding just rendering of "controls" column
Everything else is same
<v-data-table :headers="headers" :items="search_result">
<template v-slot:item.controls="props">
<v-btn class="mx-2" fab dark small color="pink" #click="onButtonClick(props.item)">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</template>
</v-data-table>
In my case the solution of Michal was throwing the following exception
The solution was using slot and slot-scope
<template>
<v-data-table
:headers="headers"
:items="items"
class="elevation-1"
>
<template slot="item.delete" slot-scope="props">
<v-btn class="mx-2" icon #click="() => delete(props.item)">
<v-icon dark>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
</template>
<script>
export default {
data() {
return {
headers: [
// Dynamic headers
{
text: 'Name',
value: 'name'
},
{
text: '',
// Row to replace the original template
value: 'delete'
},
],
items: [
{
id: 1,
name: 'A'
},
{
id: 2,
name: 'B'
}
]
};
},
methods: {
delete(item) {
this.items = this.items.filter((d) => d.id !== item.id);
},
},
};
</script>

How to get item value in v-slot:cell() template of b-table - BootstrapVue

I'm a very new at programming. I'm trying to figure it out how to bind the data to get the link :href work using store, vuex and bootstrap-vue table. I have spent 4 days for this, and now I'm dying. Please help.
books.js(store, vuex)
books: [
{
id: 1,
name: "name 1",
bookTitle: "book1",
thumbnail: '../../assets/img/book01.jpeg',
url: "https://www.google.com",
regDate: '2019-10'
},
{
id: 2,
name: "name2",
bookTitle: "book2",
thumbnail: "book2",
url: "http://www.yahoo.com",
regDate: '2019-10'
},
BookList.vue
<script>
export default {
name: "BookList",
components: {
},
computed: {
fields() {
return this.$store.state.fields
},
books() {
return this.$store.state.books
},
bookUrl() {
return this.$store.state.books.url
}
},
data() {
return {
itemFields: this.$store.state.fields,
items: this.$store.state.books,
//url: this.$store.state.books.url
}
}
};
</script>
<template>
<b-container>
<b-table striped hover :items="items" :fields="itemFields" >
<template v-slot:cell(thumbnail)="items">
<img src="" alt="image">
</template>
<template v-slot:cell(url)="items">
<b-link :href="bookUrl" >link</b-link>
</template>
</b-table>
</b-container>
</template>
The cell slot contains two properties you're generally interested in:
item (the current row, or, to be exact, the current item in items)
value (the cell - or, to be exact, the value of the current column within the item).
Therefore, considering your data, in the case of v-slot:cell(url)="{ value, item }", value is equivalent to item.url
Any of these would work:
<template v-slot:cell(url)="{ value }">
<b-link :href="value">link</b-link>
</template>
<template v-slot:cell(url)="slot">
<b-link :href="slot.value">{{ slot.item.bookTitle }}</b-link>
</template>
<template v-slot:cell(url)="{ item }">
<b-link :href="item.url">{{ item.bookTitle }}</b-link>
</template>
Working example here.
Note your question contains a few minor issues which might prevent your code from working (itemFields is referenced but not defined, not using proper getters, etc...). For details have a look at the working example.
And read the docs!