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 中的服务器端搜索和数据表排序

如果我们需要服务器端searchsortvuetify.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-tablesearch 属性。 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版本

Vue JS (Vuetify) 用 slot props 实现双向数据绑定和响应式

从 API 检索数据并将它们传递到 Vuetify 表中