I'm using vuetify for my datatable. Pagination and sort are working except the search filter. The response data from search filter is correct but the problem is its not rendering the response to my template. In vuetify docs theres only have for pagination and sort. I'm trying to implement the search function via server-side.
My User.vue
export default{
data () {
return {
max25chars: (v) => v.length <= 25 || 'Input too long!',
tmp: '',
search: '',
totalItems: 0,
pagination: {
rowsPerPage: 1,
search: ''
},
headers: [
{
text: 'Name',
sortable: true,
value: 'name',
align: 'left'
},
{
text: 'Email Add',
sortable: true,
value:'email',
align: 'left'
},
{
text: 'Roles',
sortable: true,
value:'roles_permissions',
align: 'left'
},
{
text: 'Date joined',
sortable: true,
value:'created_at',
align: 'left'
}
],
items: [],
loading: false,
timer: null
}
},
watch:{
pagination:{
handler(){
this.getDataFromApi()
.then(data => {
const self = this;
self.items = data.items;
self.totalItems = data.total;
})
},
deep: true
}
},
mounted(){
this.getDataFromApi()
.then(data => {
this.items = data.items;
this.totalItems = data.total;
});
},
methods:{
getDataFromApi(search_val){
this.loading = true;
return new Promise((resolve, reject) => {
const { sortBy, descending, page, rowsPerPage } = this.pagination
const search = this.search;
//console.log(search);
clearTimeout(this.timer);
this.timer = setTimeout(function(){
axios({
url: '/prod/api/user_table',
method:'post',
data:{
sortBy : sortBy,
descending: descending,
page : page,
rowsPerPage : rowsPerPage,
search_val : search
}
})
.then(response=>{
if(response.status == 200){
let items = response.data.data;
const total = response.data.totalRecords;
this.loading = false;
resolve({
items,
total
});
}
})
.catch(error=>{
if(error.response){
console.log(error.response);
}
})
},1000);
})
},
fetchDataFromApi(value){
//console.log(value);
}
},
created(){
}
}
Here is my back end side using laravel
public function dataTable(Request $request){
//return Datatable::eloquent(User::query())->make(true);
$sortBy = $request->sortBy;
$descending = $request->descending;
$page = $request->page;
$rowsPerPage = $request->rowsPerPage;
$search_val = $request->search_val;
//echo $rowsPerPage;
if($descending){
$orderedBy = 'desc';
}else{
$orderedBy = 'asc';
}
$start = ($page - 1) * $rowsPerPage;
/*$totalRec = User::all();
if(empty(trim($search_val))){
$user = User::orderBy($sortBy,$orderedBy)->skip($start)->take($rowsPerPage)->get();
}else{
$user = User::where([
]);
}*/
$query = User::query();
$column = ['name', 'email'];
foreach ($column as $col) {
$query->orWhere($col, 'LIKE','%'.$search_val.'%');
}
$query->orderBy($sortBy,$orderedBy)->skip($start)->take($rowsPerPage);
$arr_items = [];
foreach ($query->get()->toArray() as $shit => $v) {
$arr_items['data'][] = array(
'value' => $v['id'],
'name' => $v['name'],
'email' => $v['email'],
'roles_permissions' => '',
'created_at' => $v['created_at']
);
}
$arr_items['totalRecords'] = User::count();
return response()->json($arr_items);
}
If we need server side search and sort in vuetify.js datatable , we have to make some changes in vuejs part.
import {environment} from '../../environment';
export default {
name: "Category",
data() {
return {
categories: [],
search: '',
total: 0,
loading: false,
pagination: {},
headers: [
{text: 'ID', value: 'id'},
{text: 'Name', value: 'name'},
{text: 'Actions', value: 'name', sortable: false, align: 'center'}
],
rowsPerPageItems: [5, 10, 20, 50, 100],
}
},
watch: {
pagination {
this.getCategoriesByPagination();
},
search() {
this.getCategoriesByPagination();
}
},
methods: {
getCategoriesByPagination() {
this.loading = true;
// get by search keyword
if (this.search) {
axios.get(`${environment.apiUrl}/category-filter?query=${this.search}&page=${this.pagination.page}&per_page=${this.pagination.rowsPerPage}`)
.then(res => {
this.categories = res.data.data;
this.total = res.data.meta.total;
})
.catch(err => console.log(err.response.data))
.finally(() => this.loading = false);
}
// get by sort option
if (this.pagination.sortBy && !this.search) {
const direction = this.pagination.descending ? 'desc' : 'asc';
axios.get(`${environment.apiUrl}/category-order?direction=${direction}&sortBy=${this.pagination.sortBy}&page=${this.pagination.page}&per_page=${this.pagination.rowsPerPage}`)
.then(res => {
this.loading = false;
this.categories = res.data.data;
this.total = res.data.meta.total;
});
} if(!this.search && !this.pagination.sortBy) {
axios.get(`${environment.apiUrl}/category?page=${this.pagination.page}&per_page=${this.pagination.rowsPerPage}`)
.then(res => {
this.categories = res.data.data;
this.total = res.data.meta.total;
})
.catch(err => console.log(err.response.data))
.finally(() => this.loading = false);
}
}
}
}
in html part
<v-text-field v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
<v-data-table :headers="headers"
:items="categories"
:pagination.sync="pagination"
:total-items="total"
:rows-per-page-items="rowsPerPageItems"
:loading="loading"
></v-data-table>
in Laravel part, i used laravel scout
package.
Controller
/**
* Get category
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function getAll()
{
$per_page = empty(request('per_page')) ? 10 : (int)request('per_page');
$categories = Category::latest()->paginate($per_page);
return CategoryResource::collection($categories);
}
/**
* Get category by search results
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function getBySearch()
{
$per_page = empty(request('per_page')) ? 10 : (int)request('per_page');
$categories = Category::search(request()->query('query'))->paginate($per_page);
return CategoryResource::collection($categories);
}
/**
* Get category by sorting
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function getByOrder()
{
$per_page = empty(request('per_page')) ? 10 : (int)request('per_page');
$direction = request()->query('direction');
$sortBy = request()->query('sortBy');
$categories = Category::orderBy($sortBy, $direction)->paginate($per_page);
return CategoryResource::collection($categories);
}
Route
Route::get('category', 'Api\CategoryController@getAll');
Route::get('category-filter', 'Api\CategoryController@getBySearch');
Route::get('category-order', 'Api\CategoryController@getByOrder');
Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Category extends Model
{
use Searchable;
/**
* Get the indexable data array for the model.
*
* @return array
*/
public function toSearchableArray()
{
return [
'name' => $this->name
];
}
}
To enable server-side search to work don't pass the search prop to v-data-table. Otherwise the datatable pagination and search are client side even if you pass the "totalItems" prop.
You can pass the search prop but the initial value needs to be null. I tried it first with an empty string and it didn't work, at least in my case.
<template>
<div class="data-table">
<v-data-table :headers="headers" :items="desserts" :items-per-page="5" :options.sync="options" :server-items-length="totalDesserts" :loading="loading" class="elevation-1" ></v-data-table>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import axios from 'axios'
import { api } from '~/config'
import Form from '~/mixins/form'
export default {
data: () => ({
desserts_s: [],
totalDesserts: 0,
loading: true,
options: {},
headers: [
{ text: 'id', value: 'id' },
{ text: 'lastname', value: 'lastname' },
{ text: 'email', value: 'email' },
],
desserts: [],
}),
watch: {
options: {
handler () {
this.getDataFromApi()
.then(data => {
this.desserts = data.items
this.totalDesserts = data.total
})
},
deep: true,
},
},
mounted () {
this.getDataFromApi()
.then(data => {
this.desserts = data.items
this.totalDesserts = data.total
})
},
methods: {
getDataFromApi () {
this.loading = true
return new Promise((resolve, reject) => {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
axios.get(api.path('test')+"?"+Object.keys(this.options).map(key => key + '=' + this.options[key]).join('&'))
.then((response) => {
let items = response.data.users.data
const total = response.data.users.total
console.log(response.data.users.data)
if (sortBy.length === 1 && sortDesc.length === 1) {
items = items.sort((a, b) => {
const sortA = a[sortBy[0]]
const sortB = b[sortBy[0]]
if (sortDesc[0]) {
if (sortA < sortB) return 1
if (sortA > sortB) return -1
return 0
} else {
if (sortA < sortB) return -1
if (sortA > sortB) return 1
return 0
}
})
}
this.loading = false
resolve({
items,
total,
})
})
.catch((error) => console.log(error.message))
})
},
getDesserts () {
},
},
}
</script>
You should use computed
i'm using server pagination and search. you can inspect my code
<template>
<v-card flat>
<v-data-table
:headers="tableHead"
:items="computedFormData.items"
v-if="computedFormData && computedFormData.items"
:mobile-breakpoint="820"
v-model="selected"
:show-select="true"
:loading="loading"
:form-data="formData"
@update:page="getItemPerPage"
@update:items-per-page="getItemPerPage2"
:server-items-length="paginationTotal"
:schema="schema"
:search="search"
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title class="mr-4" v-if="addHeading">{{ addHeading }}</v-toolbar-title>
</v-toolbar>
</template>
</v-data-table>
</v-card>
</template>
<script>
import {mapMutations, mapGetters, mapActions} from 'vuex'
export default {
name: 'DataTable',
components: { Button, Tab: () => import('@/components/Tabs'), Dialog: () => import('@/components/Dialog'), TableFormBuilder: () => import('@/components/Form/TableFormBuilder'), FormBuilder: () => import('@/components/Form/FormBuilder') },
props: [
'schema',
'formData',
'name',
'itemsTab',
'value',
'headers',
'title',
'nodata',
'addHeading',
'confirmDeleteTabItem',
'tableTitleOptionA',
'tableTitleOptionB',
'items'
],
data: () => ({
loading: false,
selected: [],
companyValid: true,
customerValid: true,
search: '',
dialog: false,
editedIndex: -1,
editedItem: {},
defaultItem: {}
}),
computed: {
...mapGetters('Connection', ['getConnectionPending', 'getAddFirm', 'getUpdateFirm', 'getDeleteFirm', 'getMultipleDeleteFirm', 'getCompanies']),
...mapGetters('Pagination', ['getPage']),
tableHead(){
return this.headers.filter(s => s.show);
},
computedFormData: {
get: function () {
return this.$parent.formData
},
set: function () {
return this.formData
}
},
paginationTotal: {
get: function () {
return this.$parent.formData.totalLength
}
},
tabItems: {
get: function () {
if(this.search!==''){
return this.$parent.formData.items.filter(s => s.firmaAdi === this.search)
}else{
return this.$parent.formData.items
}
},
set: function () {
return this.items
}
},
formTitle () {
return this.editedIndex === -1
? this.tableTitleOptionA
: this.tableTitleOptionB
}
},
methods: {
...mapActions("Snackbar", ["setSnackbar"]),
...mapActions("Connection", ["addFirmCall", "updateFirmCall", "deleteFirmCall", "multipleDeleteCall", "companiesCall"]),
...mapMutations('Selected', ['setSelected']),
...mapMutations('Pagination', ['setPage']),
getItemPerPage (pagination) {
this.loading=true;
this.setPage(pagination)
},
getItemPerPage2 (pagination) {
this.loading=true;
this.setPage(pagination)
},
},
watch: {
getConnectionPending(e){
this.loading=e
},
dialog(val) {
val || this.close();
},
search(e){
this.companiesCall({ page: this.getPage, limit: 10, search: e});
},
selected(e){
this.setSelected(e)
}
},
}
</script>
To late for answer, but I was looking for something similar to work with yajra/laravel-datatables this days and didn't found any examples / libs, so created something that worked:
composer require yajra/laravel-datatables-oracle:"~9.0"
(And follow the instructions on how to add Provider, Facade, config
use DataTables;
------
public function dataTable(Request $request){
//one line of code for simple search /sort / pagination
return DataTables::of(User::query())->make(true);
}
Vuetify
component<template>
<v-data-table
:headers="headers"
:items="users"
:pagination.sync="pagination"
:total-items="totalUsers"
:rows-per-page-items="rowsPerPageItems"
:loading="loading"
>
<template v-slot:items="props">
<tr>
<td>
<div class="d-flex">
<v-btn :to="{ name: 'users.edit', params: { id: props.item.id }}">Edit</v-btn>
</div>
</td>
<td>{{ props.item.id }}</td>
<td>{{ props.item.name }}</td>
<td>{{ props.item.email }}</td>
</tr>
</template>
<template v-slot:no-results>
<v-alert :value="true" color="error" icon="warning">
Your search for "{{ searchQuery }}" found no results.
</v-alert>
</template>
</v-data-table>
</template>
<script>
import axios from 'axios';
export default {
data () {
return {
draw: 1,
users: [],
searchQuery: "",
loading: true,
pagination: {
descending: true,
page: 1,
rowsPerPage: 10,
sortBy: "id",
totalItems: 0
},
totalUsers: 0,
rowsPerPageItems: [10, 15, 20, 30, 40, 50],
columns:{},
headers: [
{ text: 'Actions', value: 'actions', sortable: false, searchable: false, width: '210px'},
{ text: 'ID', value: 'id', name: 'id', sortable: true, searchable: true, width: '40px'},
{ text: 'Name', value: 'name', name: 'name', sortable: true, searchable: true, width: '250px'},
{ text: 'Email', value: 'email', sortable: true, searchable: true, width: '80px'},
],
cancelSource: null
}
},
watch: {
//watcher to watch for order/pagination and search criteria.
//
params: {
handler() {
//on params change refetch Data
//We don't do it in mounted method, becuase on first load params will change.
this.getDataFromApi().then(data => {
this.users = data.items;
this.totalUsers = data.total;
});
},
deep: true
}
},
mounted() {
//Based on our Headers we create query data for DataTables
//I've added a new param "searchable" to let DataBales know that this column is not searchable
//You can also set name as "table.column eg users.name" if you select from more then table to avoid "Ambitious column name error from SQL"
for (var i = 0; i < this.headers.length; i++) {
this.columns[i] = {
data: this.headers[i].value,
name: (typeof(this.headers[i].name) != 'undefined' ? this.headers[i].name : this.headers[i].value),
searchable: this.headers[i].searchable,
orderable: this.headers[i].sortable,
search: {
value: '',
regex: false
}
};
}
},
//computed params to return pagination and search criteria
computed: {
params(nv) {
return {
...this.pagination,
query: this.searchQuery
};
}
},
methods: {
cancelRequest() {
//Axios cancelSource to stop current search if new value is entered
if (this.cancelSource) {
this.cancelSource.cancel('Start new search, stop active search');
}
},
getDataFromApi() {
//show loading of Vuetify Table
this.loading = true;
return new Promise((resolve, reject) => {
this.cancelRequest();
this.cancelSource = axios.CancelToken.source();
//copy current params to modify
let params = this.params;
params.length = params.rowsPerPage; //set how many records to fecth per page
params.start = params.page == 1 ? 0 : (params.rowsPerPage * (params.page - 1)); //set offset
params.search = {
value: params.query,
regex: false
}; //our search query
params.draw = this.draw;
//sorting and default to column 1 (ID)
if(params.sortBy){
params.order = {
0: {
column: _.findIndex(this.headers, {
'value': params.sortBy
}),
dir: (params.descending ? 'desc' : 'asc')
}
};
}else{
params.order = {
0: {
column: 1,
dir: 'desc'
}
};
}
params.columns = this.columns; //set our previously created columns
//fecth data
//I used here jQuery $.param() helper, becuase axios submits data as JSON Payload, and we need for data or Query params
//This can be changed
axios.get('/users?'+$.param(params), {
cancelToken: this.cancelSource.token
}).then((res) => {
this.draw++;
this.cancelSource = null;
let items = res.data.data;
let total = res.data.recordsFiltered;
resolve({
items,
total
});
}).catch((err) => {
if (axios.isCancel(err)) {
console.log('Request canceled', err.message);
} else {
reject(err);
}
}).always(() => {
this.loading = false;
});
});
}
}
}
</script>
A simple solution to make vuetify work with Laravel DataTables, for sure is not ideal, but works well. Hope this helped.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.