I'm new in vue.js, please help. I need filters in my table. Each column must have multiple select filter by values. When we click on column header - dropdown multi select opens, and we can select filtered values. For example like this Vuetify Data Table Inline Filter but this example doesn't work with vuetify 2.
My html:
<template>
<v-card class="elevation-3">
<v-card-title>
{{ other_title }}
<v-btn style="background-color: white; box-shadow: none;" @click="csvExport(other_title, otherIncidentsData)">
CSV<i class="fas fa-file-csv"></i>
</v-btn>
<v-btn text @click="exportToPdf()">
Pdf<v-icon>mdi-file-pdf-box-outline</v-icon>
</v-btn>
<v-btn text @click="showExportModal">
<v-icon>mdi-email-receive-outline</v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:search="search"
:headers="headers"
:items="otherIncidents"
:items-per-page="10"
>
<template v-slot:item.priority.name="{ item }">
<v-chip :color="getPriorityColor(item.priority.name)" dark>{{ item.priority.name }}</v-chip>
</template>
</v-data-table>
</v-card>
</template>
And JS:
export default {
mixins: [
mixin
],
data() {
return {
search: '',
title: 'MediaMyne reports',
project_title: 'PROJECTS (open at the end of the reporting period)',
new_title: 'NEW REQUESTS (created during the reporting period)',
other_title: 'OTHER REQUESTS (remaining open or changed during the reporting period)',
tabs: [
{ name: 'Projects' },
{ name: 'New Requests' },
{ name: 'Other Requests' },
],
headers: [
{
text: 'Company', align: 'start', sortable: true, value: 'customer.name', width: '14%',
},
{
text: 'Name (Costumer Contact)', align: 'start', sortable: true, value: 'reported_By_Customer_Contact.name', width: '16%',
},
{
text: 'Title', align: 'start', sortable: true, value: 'name', width: '17%',
},
{
text: 'Days open', align: 'center', sortable: true, value: 'daysOpen', width: '9%',
},
{
text: 'Days waiting', align: 'center', sortable: true, value: 'daysWaiting', width: '10%',
},
{
text: 'Workflow step', align: 'start', sortable: true, value: 'workflow_Step.name', width: '12%',
},
{
text: 'Support type', align: 'start', sortable: true, value: 'custom_Fields.customFields.custom_266', width: '12%',
},
{
text: 'Priority', align: 'start', sortable: true, value: 'priority.name', width: '10%',
},
],
otherIncidents: [],
newIncidents: [],
projectIncidents: [],
activeTab: 0,
pdfReportTitle: ''
};
},
components: {
ExportModal
},
computed: {
...mapGetters([
'isLoggedIn'
]),
otherIncidentsData() {
return this.otherIncidents.map(item => ({
Company: item.customer.name,
Costumer_contact_name: item.reported_By_Customer_Contact.name,
Title: item.name,
Days_open: item.daysOpen,
Days_waiting: item.daysWaiting,
Workflow_step: item.workflow_Step.name,
Support_type: item.custom_Fields.customFields.custom_266,
Priority: item.priority.name
}));
},
methods: {
...mapActions([
'setLoginState'
]),
getPriorityColor(priority) {
switch (priority.toLowerCase()) {
case 'critical':
return '#fc0000';
case 'high':
return '#c20202';
case 'normal':
return '#dd7417';
case 'low':
return '#318d14';
default:
return 'rgb(0,0,0,0)';
}
}
};
I'm making this on codepen these days, since I haven't found any nice example:
https://codepen.io/manuel-84/pen/NWxLLmN (work in progress)
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
dialog: false,
filters: { 'name': [], 'calories': [], 'status': [] },
activeFilters: {},
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
defaultItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
}),
computed: {
headers () {
return [
{
text: 'Dessert (100g serving)',
align: 'start',
sortable: true,
value: 'name',
filter: value => {
return this.activeFilters.name.includes(value);
}
},
{ text: 'Calories', value: 'calories',
filter: value => {
return this.activeFilters.calories.includes(value);
}
},
{ text: 'Status', value: 'status',
filter: value => {
return this.activeFilters.status.includes(value);
}
},
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Actions', value: 'actions', sortable: false },
]
},
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
},
},
watch: {
dialog (val) {
val || this.close()
},
/*filters: {
deep: true,
handler(val) {
console.log(val)
}
}*/
},
created () {
this.initialize()
},
methods: {
initialize () {
this.desserts = [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
status: 'DIET'
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
status: 'NO DIET'
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
status: 'DIET'
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
status: 'NO DIET'
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
status: 'DIET'
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
status: 'NO DIET'
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
status: 'NO DIET'
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
status: 'NO DIET'
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
status: 'DIET'
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
status: 'NO DIET'
},
];
for (col in this.filters) {
this.filters[col] = this.desserts.map((d) => { return d[col] }).filter(
(value, index, self) => { return self.indexOf(value) === index }
);
}
this.activeFilters = Object.assign({}, this.filters)
},
toggleAll (col) {
this.activeFilters[col] = this.desserts.map((d) => { return d[col] }).filter(
(value, index, self) => { return self.indexOf(value) === index }
)
},
clearAll (col) {
this.activeFilters[col] = []
},
editItem (item) {
this.editedIndex = this.desserts.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.desserts.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.desserts.splice(index, 1)
},
close () {
this.dialog = false
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
})
},
save () {
if (this.editedIndex > -1) {
Object.assign(this.desserts[this.editedIndex], this.editedItem)
} else {
this.desserts.push(this.editedItem)
}
this.close()
},
},
})
.v-list--dense .v-list-item, .v-list-item--dense {
min-height: 20px !important;
height: 2rem;
}
.v-application--is-ltr .v-list-item__action:first-child, .v-application--is-ltr .v-list-item__icon:first-child {
margin-right: .5rem !important;
}
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet"/>
<span id="app">
<v-app id="inspire">
<v-data-table :headers="headers" :items="desserts" sort-by="calories" item-key="name" show-select>
<template v-for="(col, i) in filters" v-slot:[`header.${i}`]="{ header }">
<div style="display: inline-block; padding: 16px 0;">{{ header.text }}</div>
<div style="float: right; margin-top: 8px">
<v-menu :close-on-content-click="false" :nudge-width="200" offset-y transition="slide-y-transition" left fixed style="position: absolute; right: 0">
<template v-slot:activator="{ on, attrs }">
<v-btn color="indigo" icon v-bind="attrs" v-on="on">
<v-icon small
:color="activeFilters[header.value].length < filters[header.value].length ? 'red' : 'default'">
mdi-filter-variant
</v-icon>
</v-btn>
</template>
<v-list flat dense class="pa-0">
<v-list-item-group multiple v-model="activeFilters[header.value]" class="py-2">
<template v-for="(item, i) in filters[header.value]">
<v-list-item :key="`item-${i}`" :value="item" :ripple="false">
<template v-slot:default="{ active, toggle }">
<v-list-item-action>
<v-checkbox :input-value="active" :true-value="item"
@click="toggle" color="primary" dense></v-checkbox>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item"></v-list-item-title>
</v-list-item-content>
</template>
</v-list-item>
</template>
</v-list-item-group>
<v-divider></v-divider>
<v-btn text block @click="toggleAll(header.value)">Toggle all</v-btn>
<v-btn text block @click="clearAll(header.value)">Clear all</v-btn>
</v-list>
</v-menu>
</div>
</template>
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th :colspan="headers.length">
This is a header
</th>
</tr>
</thead>
</template>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>My CRUD</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">New Item</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.protein" label="Protein (g)"></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<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.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>
<template v-slot:no-data>
<v-btn color="primary" @click="initialize">Reset</v-btn>
</template>
</v-data-table>
</v-app>
</div>
I hope the code is clear, anyway feel free to ask