I have a data table containing clickable rows. All is working well, I encountered one problem. If I want to highlight text on the row, the click event is triggered. The only thing I found that could help is the .exact modifier.
Hoping it will ignore the click handler if text is highlighted. But the click event is still triggered.
My Question: Is there a way I can highlight text on an item without triggering the click event.
Expected Result: Using #click.exact wont fire click event when highlighting text
Actual Result: Click event is fired when highlighting text, event using #click.exact
Side Note: It manages to hightlight the text, but as soon as you let the mouse button go, it triggers the click event.
<v-data-table
v-show="model.id && !editMode"
:headers="bagHeaders"
:items="bags"
class="elevation-1"
item-key="id"
:loading="bagsStatus.loading"
:pagination.sync="pagination"
>
<template slot="items" slot-scope="props">
<tr #click.exact="onBagClick(props.item.id)">
<td class="text-xs-left" v-for="header in bagHeaders" :key="header.id">{{ formatColumn(header, props.item) }}</td>
</tr>
</template>
</v-data-table>
Edit:
Other Attempts: #click.prevent also not working
Best work around so far: https://codepen.io/anon/pen/YBNLLy
OK, try #click.stop on the TDs which stops the event from propagating to the parent TR.
Since you want to preserve the normal row clicking behavior on certain condition, you could add a method for inspecting if any text-selection is being made while clicking and proceed with stopping the event propagation, otherwise invoke the onBagClick() method of the parent TR:
new Vue({
el: '#app',
methods: {
onBagClick(id) {
alert('Bag Click');
},
checkMouseAction(e) {
const isTextHighlighting = window.getSelection().toString().trim() !== '';
if (!isTextHighlighting) {
e.target.parentElement.click();
// Or call the this.onBagClick() instead
}
}
}
})
table {
border-collapse: collapse;
}
table td {
border: 1px solid;
padding: 10px;
}
table td:first-child {
background-color: lavender;
}
table td:last-child {
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr #click="onBagClick">
<td #click.stop="checkMouseAction">Selectable text. Left-click and drag these lines with your mouse.</td>
<td>Hardly selectable text. An alert dialog will get in the way by popping up.</td>
</tr>
</table>
</div>
Edits
The above will work too, but actually I figured out another obvious workaround: Do the text-selection checking on the table row level:
Working demo
new Vue({
el: '#app',
data() {
return {
headers: [
{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name'
},
{
text: 'Calories',
value: 'calories'
},
{
text: 'Fat (g)',
value: 'fat'
},
{
text: 'Carbs (g)',
value: 'carbs'
},
{
text: 'Protein (g)',
value: 'protein'
},
{
text: 'Iron (%)',
value: 'iron'
}
],
desserts: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: '1%'
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
iron: '1%'
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
iron: '7%'
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
iron: '8%'
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
iron: '16%'
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
iron: '0%'
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
iron: '2%'
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
iron: '45%'
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
iron: '22%'
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
iron: '6%'
}
]
}
},
methods: {
onBagClick(id) {
const isTextHighlighting = window.getSelection().toString().trim() !== '';
if (!isTextHighlighting) {
alert("Bag Click");
}
}
}
})
#import url('https://fonts.googleapis.com/css?family=Roboto|Material+Icons');
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.4.4/dist/vuetify.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.4.4/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-data-table :headers="headers" :items="desserts" class="elevation-1">
<template slot="items" slot-scope="props">
<tr #click="onBagClick(props.item.id)">
<td class="text-xs-left"
v-for="header in props.item"
:key="header.id">{{header}}</td>
</tr>
</template>
</v-data-table>
</v-app>
</div>
I'm using window.getSelection() for reading selected texts. If you care about supporting IE 8 (and below), have a look at this post for a fallback text-selection-acquiring approach.
Related
I have an appBar(mobile) with some icons on it. I want to show a dropdown when I click the notifications icon (as seen in many mobile apps) with the list of notifications. I am absolutely new to Vue.
<template>
<div>
<!-- <v-app-bar color="deep-purple accent-4" dense dark> -->
<v-menu>
<template v-slot:activator="{ on: activationEvents }">
<v-btn icon v-bind="attrs" v-on="activationEvents">
<div class="notification" #click="showNotif" v-bind="attrs" v-on="on">
<fas class="notif-icon" :icon="['far', 'bell']" />
<span class="count" v-if="notification.unreadCount != 0">{{
notification.unreadCount.toLocaleString()
}}</span>
</div>
</v-btn>
</template>
<v-list>
<v-list-item
v-for="dessert in desserts"
:key="dessert"
#click="() => {}"
>
<v-list-item-title> {{ dessert.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<!-- </v-app-bar> -->
</div>
</template>
export default {
data() {
return {
toggleSelect: false,
desserts: [
{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0,
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0,
},
{
name: "Eclair",
calories: 262,
fat: 16.0,
},
{
name: "Cupcake",
calories: 305,
fat: 3.7,
},
{
name: "Gingerbread",
calories: 356,
fat: 16.0,
},
{
name: "Jelly bean",
calories: 375,
fat: 0.0,
},
{
name: "Lollipop",
calories: 392,
fat: 0.2,
},
{
name: "Honeycomb",
calories: 408,
fat: 3.2,
},
{
name: "Donut",
calories: 452,
fat: 25.0,
},
{
name: "KitKat",
calories: 518,
fat: 26.015,
},
],
notification: { unreadCount: 0 },
};
},
Currently, there are 2 problems:
The list gets displayed directly without even clicking on the icon
The list gets displayed on/in the appbar (instead of getting displayed below the appbar)
I am a beginner in Vue.js and Vuetify:
I have a Vuetify data table and I am trying to append different icons (customized icons from flaticon) to just the column for example "fat (g)". Means every new line has it's own icon.
Moreover I like to have a title instead of the checkmark in the v-table-header.
How can I do this in the following code?
Example code:
https://github.com/vuetifyjs/vuetify/blob/master/packages/docs/src/examples/v-data-table/prop-row-selection.vue:
<template>
<div id="app">
<v-app id="inspire">
<v-data-table
v-model="selected"
:headers="headers"
:items="desserts"
:single-select="singleSelect"
item-key="name"
show-select
class="elevation-1"
>
<template v-slot:top>
<v-switch
v-model="singleSelect"
label="Single select"
class="pa-3"
></v-switch>
</template>
</v-data-table>
</v-app>
</div>
</template>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
singleSelect: false,
selected: [],
headers: [
{
text: 'Dessert (100g serving)',
align: 'start',
sortable: false,
value: 'name',
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Iron (%)', value: 'iron' },
],
desserts: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: '1%',
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
iron: '1%',
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
iron: '7%',
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
iron: '8%',
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
iron: '16%',
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
iron: '0%',
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
iron: '2%',
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
iron: '45%',
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
iron: '22%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
iron: '6%',
},
],
}
},
})
</script>
Check this codesanbox I made: https://codesandbox.io/s/stack-70958304-brw8z?file=/src/components/AppendIcons.vue
Well it depends, how do you want to decide what icon use for each row. The normal aproach would be to have the desired icon name within your data array.
desserts: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: '1%',
icon: 'home'
},
...
]
Now to append the icon to the fat(g) column can be done using the item slot of your v-data-table. In my example I used the #item.fat="{item}" which is a shortand for v-slot:item.fat="{item}" to modify the content of the fat column. For more info about slots check the documentation page.
If you're using es-lint you might encounter a warning mention something about the valid-v-slot rule, this is a known false positive that you can read more about in this GitHub issue thread, I personally just turn that es-lint rule off.
<v-data-table
v-model="selected"
:headers="headers"
:items="desserts"
item-key="name"
show-select
class="elevation-1"
>
<template v-slot:top>
<div class="my-2 mx-4 text-h5 font-weight-medium">
My custom title
</div>
</template>
<template #item.fat="{item}">
<div class="d-flex">
<div>
{{ item.fat }}
</div>
<div class="ml-2">
<i :class="`fi fi-rr-${item.icon}`"></i>
</div>
</div>
</template>
</v-data-table>
UPDATE 1: Import flaticon through CDN.
<style>
/* <style> blocks that doesn't have the 'scoped' attribute
make the CSS available in all your components, I usually put
my global CSS in the App.vue file or in a external css file.
In this case I'm just importing the regular rounded style
of flaticons, if you want to use bold, solid, straight icons
you'll need to import those as well */
#import url("https://cdn-uicons.flaticon.com/uicons-regular-rounded/css/uicons-regular-rounded.css");
i {
font-size: 1.2em;
}
</style>
Vuetify components use material icons by default inside of them, in case you want to go one step further and modify those icons to use flaticons. Instead of creating a wrapper component or manually defining the specific icon each time a component appears, you can configure it at a global level, see Icons Font docs for more info.
Let's say you want to modify the sort arrow icon used in vuetify components to use the arrow-small-up flaticon. Then you would do something like this:
// src/plugins/vuetify.js
import Vue from "vue";
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
Vue.use(Vuetify)
export default new Vuetify({
icons: {
values: {
sort: "fi fi-rr-arrow-small-up"
},
},
})
UPDATE 2: How to use custom SVG files in vuetify.
Local SVG files can be displayed in your applicaction by simply using the normal <img> tag. Vuetify's <v-img> component can only display external SVG files that comes from an URL. Like this one right here: https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/410.svg
That's because the internal fileloader that use <v-img> doesn't support local SVG files. So the simplest solution is just use the normal <img> tag.
<div>
{{ item.fat }}
</div>
<div class="ml-2">
<img v-if="item.name == 'Donut'" src="../assets/donut.svg" width="20" alt=""/>
<i v-else :class="`fi fi-rr-${item.icon}`"></i>
</div>
I am currently trying to pull in the datatable row info into a dialog for the rows with qty input value greater than 0. I know with checkboxes if selected/true, it pulls all the same row information into a selected array to easily use. I'm trying to get this same functionality for input greater than 0.
Got checked boxes row info to get into dialog by using the selected array. Cant seem to figure out how to do the same (get that rows data) with inputs greater than 0
https://codepen.io/anon/pen/xeVZKv?editors=1010
HTML
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
class="elevation-1"
v-model="selected"
item-key="name"
>
<template v-slot:items="props">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.calories }}</td>
<td class="text-xs-right">{{ props.item.fat }}</td>
<td class="text-xs-right">{{ props.item.carbs }}</td>
<td class="text-xs-right">{{ props.item.protein }}</td>
<td v-if="props.item.qty < 2" style="width: 10%">
<v-checkbox v-model="props.selected" primary hide-details></v-checkbox>
</td>
<td v-else style="display:inline-flex;">
<v-text-field
value="0"
onclick="this.select()"
type= "number"
:max="props.item.Qty"
:totalCount="props.item.Qty"
min="0"
:id="'inputCount' + props.index"
style="width:40px; margin-left: 5px;"
#change="counter()"
></v-text-field>
<div style="padding-top: 15px; margin-left: 10px;">of {{ props.item.qty }}</div>
</td>
</template>
<template v-slot:pageText="props">
Lignes {{ props.pageStart }} - {{ props.pageStop }} de {{ props.itemsLength }}
</template>
</v-data-table>
<v-layout row wrap class="">
<v-flex xs4 class="text-xs-left">
</v-flex>
<v-flex xs4 class="text-xs-center">
</v-flex>
<v-flex xs4 class="text-xs-right">
<v-btn v-if="buyCounter < 1" color="primary" class="receiveButton" :disabled="buttonActivate" #click="buyModel()">Receive</v-btn>
<v-btn v-else-if="buyCounter > 0 && buyCounter < 2" color="primary" class="receiveButton" :disabled="buttonActivate" #click="buyModel()">Receive {{ buyCounter }} Part</v-btn>
<v-btn v-else-if="buyCounter > 1" color="primary" class="receiveButton" :disabled="buttonActivate" #click="buyModel()">Receive {{ buyCounter }} Parts</v-btn>
<v-btn v-else color="primary" class="receiveButton" :disabled="buttonActivate">Receive</v-btn>
</v-flex>
</v-layout>
</v-app>
</div>
JS
new Vue({
el: '#app',
data () {
return {
inputCount: 0,
selected: [],
headers: [
{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name'
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Qty Available', value: 'qty' }
],
desserts: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
qty: 1
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
qty: 2
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
qty: 1
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
qty: 1
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
qty: 3
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
qty: 1
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
qty: 1
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
qty: 1
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
qty: 1
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
qty: 1
}
]
};
},
computed: {
buttonActivate: function () {
let count = 0;
$('input[id^="inputCount"]').each(function (i, e) {
let element = $(e).val();
if (element == "") {
element = 0;
}
let eachCountInt = parseInt(element);
count = eachCountInt + count;
})
this.inputCount = count;
if (this.selected.length > 0 || this.inputCount > 0) {
return false;
} else {
return true;
}
},
buyCounter: function () {
let count = this.selected.length + this.inputCount;
return count;
}
},
methods: {
counter() {
let count = 0;
$('input[id^="inputCount"]').each(function (i, e) {
let element = $(e).val();
if (element == "") {
element = 0;
}
let eachCountInt = parseInt(element);
count = eachCountInt + count;;
})
this.inputCount = count;
},
buyModel() {
}
}
})
Overall looking to pull in all selected/greater than 0 info into the dialog to confirm before processing. Any help would be greatly appreciated.
Is it possible to left align the rows per pages and page navigation and put a button in its place in the same row?
The actions row are aligned right by default. There is a way to achieve what you want though. You can use custom pagination and hide the current one:
<v-data-table
:headers="headers"
:items="desserts"
:search="search"
hide-actions
:pagination.sync="pagination"
>
And add this after the datatable:
<v-layout row justify-center>
<v-pagination v-model="pagination.page" :length="pages"></v-pagination>
<v-btn class="test">test</v-btn>
</v-layout>
See more here.
Here is a Codepen in action.
I found the easiest way is to add the button to some data table slot you use and just position the button relatively to the table.
<v-data-table
style="position: relative;">
<template slot="footer">
<v-btn style="position: absolute; left: 10px; bottom: 10px;">
test
</v-btn>
</template>
</v-data-table>
Simplest way by CSS :
template in your datatable :
<template v-slot:actions-prepend>
<v-btn>
Click me !
</v-btn>
</template>
CSS :
.my-grid .v-datatable__actions > div:first-child {
flex: 1;
}
Working snippet :
new Vue({
el: '#app',
methods: {
onClick() { this.dark = !this.dark; }
},
data: {
dark: true,
headers: [{
text: 'Dessert (100g serving)',
value: 'name'
},
{
text: 'Calories',
value: 'calories'
},
{
text: 'Fat (g)',
value: 'fat'
},
{
text: 'Carbs (g)',
value: 'carbs'
},
{
text: 'Protein (g)',
value: 'protein'
},
{
text: 'Iron (%)',
value: 'iron'
}
],
desserts: [{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: '1%'
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
iron: '1%'
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
iron: '7%'
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
iron: '8%'
}
]
}
})
.my-grid .v-datatable__actions > div:first-child {
flex: 1;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.js"></script>
<div id="app">
<v-app :dark="dark">
<v-content>
<v-data-table :headers="headers" :items="desserts" class="my-grid">
<template v-slot:items="{item}">
<tr>
<td>{{item.name}}</td>
<td>{{item.calories}}</td>
<td>{{item.fat}}</td>
<td>{{item.carbs}}</td>
<td>{{item.protein}}</td>
<td>{{item.iron}}</td>
</tr>
</template>
<template v-slot:actions-prepend>
<v-btn #click="onClick">
Switch mode
</v-btn>
</template>
</v-data-table>
</v-content>
</v-app>
</div>
In a data table, I want to display only items, where the property 'display' is 'true'. There is the property 'filter' in the component v-data-table. But there is no example showing, how to use it.
I have tried several approaches, but without success. The following code snippet is also available at codepen.
new Vue({
el: '#app',
data () {
return {
headers: [
{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name'
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Iron (%)', value: 'iron' }
],
desserts: [
{
value: false,
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: '1%',
display: false
},
{
value: false,
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
iron: '1%',
display: true
},
{
value: false,
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
iron: '7%',
display: false
},
{
value: false,
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
iron: '8%',
display: true
},
{
value: false,
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
iron: '16%',
display: false
},
{
value: false,
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
iron: '0%',
display: true
},
{
value: false,
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
iron: '2%',
display: false
},
{
value: false,
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
iron: '45%',
display: false
},
{
value: false,
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
iron: '22%',
display: false
},
{
value: false,
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
iron: '6%',
display: false
}
]
}
},
methods: {
filterItems(val, search) {
return val.display;
}
}
})
<!DOCTYPE html>
<html>
<head>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
hide-actions
item-key="name"
:filter="filterItems"
>
<template slot="items" slot-scope="props">
<tr #click="props.expanded = !props.expanded" :class="[props.expanded && 'expanded']">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.calories }}</td>
<td class="text-xs-right">{{ props.item.fat }}</td>
<td class="text-xs-right">{{ props.item.carbs }}</td>
<td class="text-xs-right">{{ props.item.protein }}</td>
<td class="text-xs-right">{{ props.item.iron }}</td>
</tr>
</template>
<template slot="expand" slot-scope="props">
<v-card flat>
<v-card-text>Peek-a-boo!</v-card-text>
</v-card>
</template>
</v-data-table>
</v-app>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.js"></script>
</body>
</html>
The problematic part of the code is:
methods: {
filterItems(val, search) {
return ???;
}
}
I was struggling for a long time with this issue because I also thought :filter did not work. Later, after a lot of pain, I realised that I needed a :search prop to use :filter, and the functionality that I wanted was not the one that is provided by Vuetify.
Like you, I wanted to filter my data-table with a function that returned a boolean, i.e: true, if row should be displayed.
If someone is looking for a quick solution to do the same, a simple "v-if" should do the trick. Based on the example from above:
<template slot="items" slot-scope="props" v-if="props.item.display">
There are other solutions of course, depending on your needs, like using .filter method on the desserts array itself.
In my case, because I wanted to always display 5 rows at a time in my data-table (not using hide-actions), I ended up using the created() lifecycle hook, instead of a v-if:
created: function() {
this.shownDesserts = this.desserts.filter(dessert => {
return dessert.display
})
},
Looking at the source code, I think you really want custom-filter as opposed to filter:
(items: object[], search: string, filter: Filter): object[]
So you would define a function and pass it as an argument to the custom-filter property on the table. When searching, you are provided with all the items in the table represented by an array of objects (object[]), the search string that was typed into the search box, and the filter function to apply against all the objects in the array, so:
:custom-filter="filterItems"
filterItems(items, search, filter) {
items.filter(r => filter(r.calories > search))
}
The above of course is just a very rudimentary example.
Here is the solution I came up with. I was having a similar problem where I wanted to implement a custom filter that still worked with the search. I ended up using a computed property that applies a filter to the data and that computed property is what gets sent to the table.
computed: {
dataListDisplay: function() {
return this.applyFilters(this.dataList);
},
}
dataListDisplay is what you send to the v-data-table items prop. And you will need to implement the applFilters function how you'd like.
My table is read only. I imagine you'd have to be careful if you are changing the data. You may need to provide a setter for the dataListDisplay computed function.
For props, filter, this should help:
methods: {
filterItems(val, search) {
return val != null && typeof val !== 'boolean' && val.toString().toLowerCase().indexOf(search) !== -1;
}
}