laravel Vue js:Vuetify 服务器端数据表搜索过滤器不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47630856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 17:02:36  来源:igfitidea点击:

Vue js: Vuetify server side Datatable search filter not working

javascriptlaraveldatatablevue.jsvuetify.js

提问by BGTabulation BGTabulate

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 vuetifydocs theres only have for pagination and sort. I'm trying to implement the search function via server-side.

我正在为我的数据表使用 vuetify。除了搜索过滤器外,分页和排序都在工作。来自搜索过滤器的响应数据是正确的,但问题是它没有将响应呈现给我的模板。在vuetify文档中,只有分页和排序。我正在尝试通过服务器端实现搜索功能。

My User.vue

我的用户.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

这是我使用 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);
}

回答by Eliyas Hossain

server side search & sort of datatable in vuetify.js

vuetify.js 中的服务器端搜索和数据表排序

If we need server side searchand sortin vuetify.js datatable, we have to make some changes in vuejs part.

如果我们需要在vuetify.js 数据表中进行服务端搜索排序,我们必须在 vuejs 部分进行一些更改。

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 htmlpart

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>

in Laravelpart, i used laravel scoutpackage.

Laravel部分,我使用了laravel scout包。

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
            ];
         }
    }

回答by user219839

    <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>

回答by Nicolas Bonnici

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.

要启用服务器端搜索,请不要将搜索道具传递给 v-data-table。否则,即使您传递了“totalItems”道具,数据表分页和搜索也是客户端。

回答by JoHansen

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.

您可以传递搜索道具,但初始值需要为空。我首先用空字符串尝试了它,但它不起作用,至少在我的情况下。

回答by Froxz

To late for answer, but I was looking for something similar to work with yajra/laravel-datatablesthis days and didn't found any examples / libs, so created something that worked:

回答晚了,但我这几天一直在寻找类似于yajra/laravel-datatables 的东西,但没有找到任何示例/库,所以创建了一些有用的东西:

  1. Install composer require yajra/laravel-datatables-oracle:"~9.0"(And follow the instructions on how to add Provider, Facade, config
  2. We will need to change our controller to Support DataTables:
  1. 安装composer require yajra/laravel-datatables-oracle:"~9.0"(并按照有关如何添加Provider, Facade, config
  2. 我们需要将控制器更改为支持数据表:
use DataTables;
------
public function dataTable(Request $request){
    //one line of code for simple search /sort / pagination
    return DataTables::of(User::query())->make(true);
}
  1. Next we will adjust our Vuetifycomponent
  1. 接下来我们将调整我们的Vuetify组件

Template

模板

<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

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>

Conclusion

结论

A simple solution to make vuetify work with Laravel DataTables, for sure is not ideal, but works well. Hope this helped.

使 vuetify 与 Laravel DataTables 一起工作的简单解决方案肯定不是理想的,但效果很好。希望这有帮助。