Reputation: 133
sortable and filterable not work in dynamic data from API in DataTable vuetify
its works for static data.
in laravel api :
$posts = Post::select('id', 'title', 'type', 'active', 'category_id')
->with('category')
->where('author_id', $request['userId'])
->orderBy('created_at', 'desc')
->paginate($size);
in client :
<template>
<main>
<v-app>
<!--breadcrumbs-->
<breadcrumbs :breadcrumbs="breadcrumbs"></breadcrumbs>
<!---->
<!--actions-->
<div class="actions container">
<v-btn class="action__button" depressed color="success" @click="gotoNewCategory">
<v-icon dark> mdi-plus </v-icon>
insert category
</v-btn>
</div>
<!---->
<v-card style="margin: 15px; padding: 10px">
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="search"
single-line
hide-details
></v-text-field>
</v-card>
<!--datatable-->
<v-data-table
:headers="headers"
:items="categorys"
:page="page"
:pageCount="numberOfPages"
:options.sync="options"
:server-items-length="totalCategorys"
:loading="loading"
:search="search"
class="elevation-1"
no-data-text="no data!"
:footer-props="{
showFirstLastPage: true,
'items-per-page-text': 'test',
'items-per-page-all-text': 'all',
'page-text': '',
}"
>
<template v-slot:top>
<v-dialog v-model="dialogDelete" max-width="500px">
<v-card>
<v-card-title class="headline">
</v-card-title
>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="closeDelete">no</v-btn>
<v-btn color="blue darken-1" text @click="deleteCategoryConfirm"
>yes</v-btn
>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<template v-slot:[`item.actions`]="{ item }">
<td>
<v-icon small class="mr-2" @click="editCategory(item)"> mdi-pencil </v-icon>
<v-icon small @click="deleteCategory(item)"> mdi-delete </v-icon>
</td>
</template>
</v-data-table>
<!---->
</v-app>
</main>
</template>
<script>
import breadcrumbs from "~/components/breadcrumbs";
export default {
name: "categorys",
layout: "dashboard",
components: { breadcrumbs },
data() {
return {
search: "",
page: 1,
totalCategorys: 0,
numberOfPages: 0,
dialogDelete: false,
editedIndex: -1,
categoryIdForDelete: "",
loading: true,
options: {},
headers: [
{ text: "ID", value: "id", align: "start" },
{ text: "categoryName", value: "categoryName", align: "start" },
{ text: "actions", value: "actions", sortable: false },
],
categorys: [],
};
},
//this one will populate new data set when user changes current page.
watch: {
options: {
handler() {
this.readDataFromAPI();
},
},
deep: true,
},
methods: {
readDataFromAPI() {
this.loading = true;
const { page, itemsPerPage, sortBy, sortDesc } = this.options;
let pageNumber = page;
this.$axios
.get(
"/api/category/categories?size=" +
itemsPerPage +
"&page=" +
pageNumber +
"&sortby=" +
sortBy +
"&sortDesc=" +
sortDesc,
this.fields
)
.then((response) => {
this.loading = false;
this.categorys = response.data.result.data;
this.totalCategorys = response.data.result.total;
this.numberOfPages = Math.round(response.data.result.total / itemsPerPage);
});
},
filterOnlyCapsText(value, search, item) {
return (
value != null &&
search != null &&
typeof value === "string" &&
value.toString().toLocaleUpperCase().indexOf(search) !== -1
);
},
gotoNewCategory() {
this.$router.push("/dashboard/categorys/new");
},
editCategory(item) {
this.$router.push({ name: "dashboard-categorys-edit", params: item });
},
deleteCategory(category) {
this.categoryIdForDelete = category.id;
this.editedIndex = this.categorys.indexOf(category);
this.dialogDelete = true;
},
deleteCategoryConfirm() {
this.categorys.splice(this.editedIndex, 1);
this.$axios
.delete("api/category/delete?id=" + this.categoryIdForDelete)
.then((response) => {
if (response.data.status === "success") {
this.$toast.show(response.data.message, {
theme: "toasted-primary",
position: "bottom-left",
type: "success",
duration: 2000,
});
} else {
let list = [];
$.each(response.data.message, function (key, value) {
list.push(value);
});
this.$toast.show(list, {
theme: "toasted-primary",
position: "bottom-left",
type: "error",
duration: 4000,
});
}
});
this.closeDelete();
},
closeDelete() {
this.dialogDelete = false;
this.$nextTick(() => {
this.editedIndex = -1;
});
},
},
mounted() {
this.readDataFromAPI();
},
filters: {
striphtmltag: function (value) {
var div = document.createElement("div");
div.innerHTML = value;
var text = div.textContent || div.innerText || "";
return text;
},
},
};
</script>
<style lang="scss" scoped>
* {
font-family: YekanBakh !important;
letter-spacing: normal !important;
}
button {
&:focus {
outline: none !important;
}
}
.elevation-1 {
box-shadow: 0 0.46875rem 2.1875rem rgba(4, 9, 20, 0.03),
0 0.9375rem 1.40625rem rgba(4, 9, 20, 0.03), 0 0.25rem 0.53125rem rgba(4, 9, 20, 0.05),
0 0.125rem 0.1875rem rgba(4, 9, 20, 0.03) !important;
margin: 15px;
}
</style>
filterable not pass to API variable.
but in option for sortable :
groupBy: Array(0) groupDesc: Array(0) itemsPerPage: 10 multiSort: false mustSort: false page: 1 sortBy: Array(0) sortDesc: Array(0)
Upvotes: 1
Views: 1146
Reputation: 973
The issue here is that you are using server-items-length. For Vuetify, this means you will be responsible for filtering and sorting the data in the source/back-end (the API call response data will be returning the filtered and sorted result)
As mentioned in Vuetify Data Table documentation, you must implement your own filtration and sorting logic when using server-items-length.
Here is what you can do
Update your readDataFromAPI method to include the "search" data property in the request parameters to your API.
You can add another watcher for the data property "search", and call the method readDataFromAPI in it (I don't like this implementation, as it will be calling the back-end with each character, which will be an expensive operation. Still depends on your use case)
search(val) {
this.readDataFromAPI()
},
A better implementation is to add an event listener to the append icon on the search text-field to call the readDataFromAPI (Make sure you are not calling it with an empty search value)
<v-text-field
v-model="search"
append-icon="mdi-magnify"
@click:append="readDataFromAPI()"
placeholder="Type your search and press Enter"
@keydown.enter="readDataFromAPI()"
label="search"
single-line
hide-details
></v-text-field>
Good luck.
Upvotes: 2