【问题标题】:Vue js: Vuetify server side Datatable search filter not workingVue js:Vuetify 服务器端数据表搜索过滤器不起作用
【发布时间】: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 定义属性的模板。您定义模板的方式可能有问题。

标签: javascript laravel datatable vue.js vuetify.js


【解决方案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 类似的东西,但没有找到任何示例/库,所以创建了一些有用的东西:

          1. 安装composer require yajra/laravel-datatables-oracle:"~9.0"(并按照说明如何添加Provider, Facade, config
          2. 我们需要将控制器更改为支持 DataTables:
          use DataTables;
          ------
          public function dataTable(Request $request){
              //one line of code for simple search /sort / pagination
              return DataTables::of(User::query())->make(true);
          }
          
          1. 接下来我们将调整我们的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 一起工作的简单解决方案,肯定不是理想的,但效果很好。希望这会有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-11-24
            • 1970-01-01
            • 2019-03-03
            • 2020-05-16
            • 1970-01-01
            • 2017-04-17
            • 2015-03-12
            相关资源
            最近更新 更多