Vuetify insert action button in data-table and get row data - vue.js

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>

Related

Vuetify table - add hyperlink to column

In my vue component I am using vuetify table. It works properly, but I need to make that click on items in column "Category name" redirect to vue view which shows details of clicked item (I need to forward item id to view which shows details of item). I don`t know how to do that?
In method 'editItem' I also need to redirect to other page, with forwarded id, I also don`t know how to do that?
I tried to make "Category name" items as hyperlink, but it does not work and it is without id:
<template v-slot:item.categoryName="{ item }">
<a :href="this.$router.push({name: 'EditCategory'})">{{item.categoryName}}</a>
</template>
This is code of my vue component 'CategoryTable':
<template>
<v-data-table
:headers="headers"
:items="categories"
sort-by="categoryName"
class="elevation-1"
:footer-props="{
'items-per-page-options': [1, 2, 3, 4, 5]
}"
:items-per-page="2"
>
<template v-slot:top>
<v-toolbar
flat
>
<v-toolbar-title>Categories Table</v-toolbar-title>
<v-divider
class="mx-4"
inset
vertical
></v-divider>
<v-spacer></v-spacer>
<button #click="$router.push({name: 'AddCategory'})">Add category</button>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon
small
class="mr-2"
#click="editItem(item)"
>
mdi-pencil
</v-icon>
<v-icon
small
#click="deleteItem(item)"
>
mdi-delete
</v-icon>
</template>
</v-data-table>
</template>
<script>
export default {
name: "CategoriesTable",
data: () => ({
headers: [
{
text: 'Category name',
align: 'start',
value: 'categoryName',
},
{ text: 'Description', value: 'description' },
{ text: 'Actions', value: 'actions', sortable: false },
],
categories: [],
editedIndex: -1,
defaultItem: {
id: 0,
categoryName: '',
description: '',
},
}),
created () {
this.initialize()
},
methods: {
initialize () {
this.$axios.get('/api/categories').then((response) => {
console.log(response.data)
this.categories = response.data;
});
},
editItem (item) {
this.$router.push({name: 'CategoryEdit'});
item.id
},
deleteItem (item) {
this.$axios.delete('/api/categories/' + item.id).then((response) => {
console.log(response)
});
},
},
}
</script>
If you want the user to be redirected after clicking on a particular category name, you will pass the corresponding route or id like i did below:
<v-data-table
:headers="headers"
:items="categories"
sort-by="categoryName"
class="elevation-1"
:footer-props="{
'items-per-page-options': [1, 2, 3, 4, 5],
}"
:items-per-page="2"
>
<template v-slot:item.categoryName="{ item }">
<a
#click="$router.push(`/details/${item.itemID}`)"
>
{{item.categoryName}}
</a>
</template>
</v-data-table>
the item id is passed after clicking on the category

How to add icon to the Header in vuetify DataTable

I'm trying to add (+) icon to the one of the header columns in v-datatable.
The code below does not add any icon. Actually, creating template slot for header does not have any effect on datatable.
What I try,
<template>
<v-data-table
item-key="name"
:items="complainantInfos"
:headers="headers"
sort-by="Id"
class="elevation-1">
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>Inspection</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
</template>
<template slot="headers" slot-scope="props">
<tr>
<th
v-for="header in props.headers"
:key="header.text">
<v-icon small >plus-circle-outline</v-icon>
{{ header.text }}
</th>
</tr>
</template>
</v-data-table>
</template>
<script>
import { mapGetters } from "vuex";
export default {
data(){
return{
complainantInfos:[
{
...
}
],
headers: [
{ text: 'NameSurname', value: 'name', align: 'left' ,width: "25%" },
{ text: 'ID', value: 'PersonelIdNumber' },
{ text: 'Phone-1', value: 'Cellular1' },
{ text: 'Phone-2', value: 'Cellular2' },
{ text: 'Work Phone', value: 'WorkPhone' },
{ text: 'InterPhone', value: 'Interphone' },
{ text: 'Email', value: 'Email' },
//{ text: '+', value: 'action', sortable: false, align: 'right'}
],
I have edited the code according to the comments. The problem solved.
...
</v-card>
</v-form>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:header.Actions="{ header }">
<v-icon small>plus-circle-outline</v-icon>{{ header.text }}
</template>
...
Since you only wish to add the icon to one specific column, I would suggest header.columnName.
Your slot would look like this:
<template v-slot:header.name="{ header }">
<v-icon small>plus-circle-outline</v-icon>{{ header.text }}
</template>
If the column name is "Cellular1", the code will be <template v-slot:header.Cellular1="{ header }">.
Please make sure you have the icon set included. Otherwise, no HTML will be rendered for v-icon. You can test it with default mdi icons, for example mdi-minus.

Value of v-text-field returns null when text area has a value

I'm trying to get the value from vuetify's v-text-field. The problem I am having is that I thought by typing something in, the field will automatically assign whatever is typed to vue-text-field's value. However this doesn't seem to be the case. Below is the code directly from vuetify with some additional code attempting to extract the value:
<template>
<v-card
class="overflow-hidden"
color="purple lighten-1"
dark
>
<v-toolbar
flat
color="purple"
>
<v-icon>mdi-account</v-icon>
<v-toolbar-title class="font-weight-light">
{{setterTitle}}
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn
color="purple darken-3"
fab
small
#click="isEditing = !isEditing"
>
<v-icon v-if="isEditing">
mdi-close
</v-icon>
<v-icon v-else>
mdi-pencil
</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-text-field ref="rowCount"
:disabled="!isEditing"
:value="2"
autofocus
color="white"
label="Number of Rows"
></v-text-field>
<v-autocomplete
:disabled="!isEditing"
:items="states"
:filter="customFilter"
color="white"
item-text="name"
label="Number of Columns"
></v-autocomplete>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
:disabled="!isEditing"
color="success"
#click="save"
>
Save
</v-btn>
</v-card-actions>
<v-snackbar
v-model="hasSaved"
:timeout="2000"
absolute
bottom
left
>
Your profile has been updated
</v-snackbar>
</v-card>
</template>
<script>
export default {
props: {
setterTitle: String
},
data () {
return {
hasSaved: false,
isEditing: null,
model: null,
states: [
{ name: 'Single', abbr: 'S', id: 1 },
{ name: 'Double', abbr: 'D', id: 2 },
{ name: 'Triple', abbr: 'T', id: 3 },
{ name: 'Custom', abbr: 'C', id: 0 },
// { name: 'New York', abbr: 'NY', id: 5 },
],
}
},
methods: {
customFilter (item, queryText, itemText) {
const textOne = item.name.toLowerCase()
const textTwo = item.abbr.toLowerCase()
const searchText = queryText.toLowerCase()
console.log(textOne, textTwo,itemText)
return textOne.indexOf(searchText) > -1 ||
textTwo.indexOf(searchText) > -1
},
save () {
this.isEditing = !this.isEditing
this.hasSaved = true
console.log(this.$refs.rowCount.value)
},
},
}
</script>
As you can see, in the v-text-field tag I added a ref. I then tried to use that reference to extract the value from the save() method. Provided that I also have :value="2" assigned, this.$refs.rowCount.value always return 2. However if I don't have the value property set as 2 then this.$refs.rowCount.value returns null even though I've typed something in the v-text-field. I'm guessing I am misunderstanding something here. Maybe when I type to the field, it doesn't automatically assign what is typed to the value? Thank you for your help.
I've attached the api for v-text-field https://vuetifyjs.com/en/api/v-text-field/#component-pages
v-text-field is designed as common custom input control in Vue - it has a value prop and a input event which means it can by used with v-model - see the docs how that works
As a user, you do this:
<template>
<v-text-field v-model="text" />
</template>
<script>
export default {
data: function() {
return {
text: '' // initial value
}
}
}
</script>
To your question - value is a prop. All props in Vue are one way only - parent can send data to the component (and change that data in the future) but child component cannot change value of the prop it receives. That's why when you type something into the textbox the v-text-field must use an input event to tell the parent component that something has changed and that parent should update "it's own source" of value prop...
v-model is Vue's "syntactic sugar" how to work with these two more easily...

Can Vue.Draggable be used with Vuetify v-data-table and allow utilisation of table v-slot:item.<name>?

Vuetify v-data-table supports several types of slots: v-slot:body, v-slot:item and v-slot:item.<name>.
We have been using v-slot:item.<name> extensively, as these provides a flexible way to style and process content in individual columns AND allow the table headers to be programmatically changed.
I'd like to add draggability to my v-data-table rows and have got this working using Vue.Draggable.
However the draggable component requires use of the v-data-table v-slot:body i.e. taking control of the full body of the table and thereby losing the flexibility of v-slot:item.<name>.
Is there a way these two components can be used together and provide v-slot:item.<name> support?
I have created a DataTableRowHandler component which allows v-slot:item.<name> support.
This is placed inside the draggable component, inserts the table <tr> element and feeds off the same "headers" array to insert <td> elements and v-slot:item.<name> entries. If no v-slot:item.<name> is defined then the cell value is output, in the same way that v-data-table works.
Here is the example component usage:
<v-data-table
ref="myTable"
v-model="selected"
:headers="headers"
:items="desserts"
item-key="name"
class="elevation-1"
>
<template v-slot:body="props">
<draggable
:list="props.items"
tag="tbody"
:disabled="!allowDrag"
:move="onMoveCallback"
:clone="onCloneCallback"
#end="onDropCallback"
>
<data-table-row-handler
v-for="(item, index) in props.items"
:key="index"
:item="item"
:headers="headers"
:item-class="getClass(item)"
>
<template v-slot:item.lock="{ item }">
<v-icon #click="item.locked = item.locked ? false : true">{{
item.locked ? "mdi-pin-outline" : "mdi-pin-off-outline"
}}</v-icon>
</template>
<template v-slot:item.carbs="{ item }">
{{ item.carbs }}
<v-icon>{{
item.carbs > 80
? "mdi-speedometer"
: item.carbs > 45
? "mdi-speedometer-medium"
: "mdi-speedometer-slow"
}}</v-icon>
</template>
</data-table-row-handler>
</draggable>
</template>
</v-data-table>
Here is the DataTableRowHandler component code
<template>
<tr :class="getClass">
<td v-for="(header, index) in headers" :key="index">
<slot :item="item" :name="columnName(header)">
<div :style="getAlignment(header)">
{{ getNonSlotValue(item, header) }}
</div>
</slot>
</td>
</tr>
</template>
<script>
export default {
name: "DataTableRowHandler",
components: {},
props: {
itemClass: {
type: String,
default: "",
},
item: {
type: Object,
default: () => {
return {};
},
},
headers: {
type: Array,
default: () => {
return [];
},
},
},
data() {
return {};
},
computed: {
getClass() {
return this.itemClass;
}
},
methods: {
columnName(header) {
return `item.${header.value}`;
},
getAlignment(header) {
const align = header.align ? header.align : "right";
return `text-align: ${align}`;
},
getNonSlotValue(item, header) {
const val = item[header.value];
if (val) {
return val;
}
return "";
},
},
};
</script>
An example of it's use is in this codesandbox link
I checked the source code of VDataTable and found that the content in tbody is generated by genItems().
The following example is implemented with functional components and is fully compatible with v-slot:item*:
<v-data-table ref="table" ...>
<template #body="props">
<draggable
v-if="$refs.table"
tag="tbody"
:list="props.items"
>
<v-nodes :vnodes="$refs.table.genItems(props.items, props)" />
</draggable>
</template>
<template #item.name="{ item }">
...
</template>
</v-data-table>
Here is the definition of the VNodes component:
components: {
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes,
}
}

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