Vue js:Vuetify 服务器端数据表搜索过滤器不起作用
Posted
技术标签:
【中文标题】Vue js:Vuetify 服务器端数据表搜索过滤器不起作用【英文标题】:Vue js: Vuetify server side Datatable search filter not working 【发布时间】:2018-05-17 18:29:33 【问题描述】:我正在为我的数据表使用 vuetify。除搜索过滤器外,分页和排序都在工作。来自搜索过滤器的响应数据是正确的,但问题是它没有将响应呈现给我的模板。在vuetify 文档中只有分页和排序。我正在尝试通过服务器端实现搜索功能。
我的用户.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()
这是我使用 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);
【问题讨论】:
你解决了这个问题吗? 您的问题缺少重要部分:您为v-data-table
定义属性的模板。您定义模板的方式可能有问题。
【参考方案1】:
vuetify.js 中的服务器端搜索和数据表排序
如果我们需要服务器端search和sort在vuetify.js datatable中,我们必须在vuejs部分做一些改变。 p>
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);
在html部分
<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>
在 Laravel 部分,我使用了laravel scout
包。
控制器
/**
* 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::get('category', 'Api\CategoryController@getAll');
Route::get('category-filter', 'Api\CategoryController@getBySearch');
Route::get('category-order', 'Api\CategoryController@getByOrder');
型号
<?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
];
【讨论】:
好吧,实际上如果你想使用服务器端搜索,那么你不应该使用v-data-table
的search
属性。 search
属性激活客户端渲染,因此您的数据集在服务器上和客户端上被过滤两次(这可能会产生奇怪的结果)。否则答案很好。每当您想运行服务器端搜索时,您只需提交一个 XHR 请求并使用您获取的过滤数据更新您的 items
模型。
如果我想去上一页和下一页怎么办?
@ZiaurRahman 我们在这里关注pagination
财产。当您转到上一页或下一页时,它会自动调用您的 API。【参考方案2】:
要启用服务器端搜索,请不要将搜索属性传递给 v-data-table。否则,即使您传递了“totalItems”属性,数据表的分页和搜索也是客户端。
【讨论】:
【参考方案3】:您可以传递 search 属性,但初始值必须为 null。我先用一个空字符串尝试了它,但它不起作用,至少在我的情况下。
【讨论】:
【参考方案4】: <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>
【讨论】:
【参考方案5】:你应该使用计算的
我正在使用服务器分页和搜索。你可以检查我的代码
<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>
【讨论】:
【参考方案6】:回答迟了,但我这几天一直在寻找与 yajra/laravel-datatables 类似的东西,但没有找到任何示例/库,所以创建了一些有用的东西:
-
安装
composer require yajra/laravel-datatables-oracle:"~9.0"
(并按照说明如何添加Provider, Facade, config
我们需要将控制器更改为支持 DataTables:
use DataTables;
------
public function dataTable(Request $request)
//one line of code for simple search /sort / pagination
return DataTables::of(User::query())->make(true);
-
接下来我们将调整我们的
Vuetify
组件
模板
<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>
JS
<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>
结论
让 vuetify 与 Laravel DataTables 一起工作的简单解决方案,肯定不是理想的,但效果很好。希望这会有所帮助。
【讨论】:
以上是关于Vue js:Vuetify 服务器端数据表搜索过滤器不起作用的主要内容,如果未能解决你的问题,请参考以下文章
Vue.js - 如何访问子组件的计算属性(Vuetify 数据表)
无法从自定义自动完成搜索栏中选择项目 (Vue.js/Vuetify.js)
使用 laravel Inertia JS Vuetify DataTable
Vue和Material Design开源框架Vuetify发布2.0 release版本