Issue with Vuetify server side Datatable filtering in Vue.js未解决

I'm currently utilizing vuetify to handle my datatable functionality, including pagination and sorting. However, I've encountered an issue with the search filter. Although the response data from the search filter is correct, it's not getting rendered on my template. The documentation for vuetify only covers pagination and sorting options. My goal is to implement a server-side search function for enhanced functionality.

This is my User.vue file:

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(){

  }
}

On the backend side, here's how I'm handling it 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);
}

Answer №1

Implementing Server-Side Search and Sorting in Vuetify.js Datatable

To enable server-side search and sort functionality in a Vuetify.js datatable, adjustments need to be made in the Vue.js code.

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;
            // Fetch data by search query
            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);
            }
            // Fetch data 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 the HTML section:

<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 the Laravel part, the laravel scout package is utilized.

Controller

/**
 * Retrieve all categories
 * @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);
}

/**
 * Retrieve categories based on 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);
}

/**
 * Retrieve categories based on 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
            ];
         }
    }

Answer №2

If you want server-side search functionality to function properly, do not include the "search" prop when using v-data-table. Otherwise, even if you provide the "totalItems" prop, the pagination and search for the data table will remain client-side.

Answer №3

When passing the search prop, make sure to set the initial value as null instead of an empty string. I learned this the hard way when it didn't work for me initially with an empty string.

Answer №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.getAPIData()
                .then(data => {
                  this.desserts = data.items
                  this.totalDesserts = data.total
                })
            },
            deep: true,
          },
        },
        mounted () {
          this.getAPIData()
            .then(data => {
              this.desserts = data.items
              this.totalDesserts = data.total
            })
        },
            methods: {
          getAPIData () {
            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))
            })
          },
          getData () {


          },
        },
      }

    </script>

Answer №5

It is recommended to utilize the computed property

When implementing server pagination and search functionality, you can review the code snippet below

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

Answer №6

While it may be too late for a response, I recently found myself in search of a solution similar to what yajra/laravel-datatables offers. Unfortunately, I couldn't find any examples or libraries that suited my needs, so I took matters into my own hands and created something from scratch:

  1. Start by installing
    composer require yajra/laravel-datatables-oracle:"~9.0"
    (Follow the instructions to add Provider, Facade, config)
  2. We need to modify our controller to support DataTables:
use DataTables;
------
public function dataTable(Request $request){
    //Just one line of code for simple search, sort, and pagination
    return DataTables::of(User::query())->make(true);
}
  1. Next, let's make adjustments to our Vuetify component

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

<script>
    import axios from 'axios';
    export default {

        data () {
            return {
                // Data goes here
            }
        },

        watch: {
            // Watcher logic goes here
        },

        mounted() {
            // Logic for mounting goes here
        },

        computed: {
            // Computed properties go here
        },

        methods: {
            // Methods go here
        }
    }
</script>

Conclusion

In conclusion, this customized solution enables Vuetify to seamlessly integrate with Laravel DataTables. While it may not be perfect, it gets the job done efficiently. I hope this information proves helpful to others facing similar challenges.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Upon my initial click of the delete button, all the notes vanished. However, after refreshing the page, everything was back to normal and functioning properly

After numerous attempts, I have exhausted almost all troubleshooting methods. I meticulously tested my API to rule out any issues related to it. Strangely, the problem only occurs upon initial page visit, but works flawlessly upon refreshing. Prior to cli ...

Executing JavaScript code on ASP.NET page load

Inside my HTML code, there is a radio box styled using ASP.NET RadioButtonList with specific attributes. The second list item is set to be selected by default, however, the problem arises when the page loads as the function dis() is not being called. I wan ...

Is there a way to run a node script from any location in the command line similar to how Angular's "

Currently, I am developing a node module that performs certain functions. I want to create a command similar to Angular's ng command. However, I am facing compatibility issues with Windows and Linux operating systems. Despite my attempts to modify the ...

Analyzing refined information using D3

After loading data, I implemented a filter feature using checkboxes. Every time the checkbox is updated, the date field in the data needs to be parsed. The script below achieves this successfully on the first click but encounters an error on subsequent c ...

Enhance CKEditor with Linked Select Boxes Plugin

I have ventured into writing a CKEditor Plugin and have grasped the basic concepts. For instance: CKEDITOR.dialog.add( 'addDocumentGroupDialog', function ( editor ) { return { title: 'Link to a document group', min ...

Having trouble with TypeScript Library/SDK after installing my custom package, as the types are not being recognized

I have created my own typescript library using the typescript-starter tool found at . Here is how I structured the types folder: https://i.stack.imgur.com/igAuj.png After installing this package, I attempted a function or service call as depicted in the ...

Expanding worldwide mixin in Nuxt.js

Currently, I am working with Nuxtjs version 2.15.7 and have a mixin file structured like this: utils.js : import Vue from "vue" if (!Vue.__utils__) { Vue.__utils__ = true Vue.mixin({ data(){ return{ ... } }, ...

What is the best way to change a String into an Array within MongoDB?

I need help with converting the type of an object that has been changed. Is there a way to transform this: { "_id" : NumberLong(257), "address" : "street Street, house 50, appartment 508, floor 5" } into this: { "_id" : NumberLong(257), "userAddres ...

Encountering a issue of not finding the cli.json file while setting up a fresh Vue project with

Encountering issues with missing cli.json file while trying to initialize a new Vue project using Amplify. Following the steps in the official tutorial Managed to get to the point of Initializing a new backend and executing amplify init: https://i.sstati ...

Discover the #ID and apply AddClass through the URL in the Navigation Jquery

I am currently in the process of developing a website and I am looking to navigate from one link to another using IDs. Here is an example of what I am trying to achieve: Page Name: index.html <a href= "collection.html#rings">Rings</a> Page N ...

Is it possible for me to declare attributes using a function object in a single statement?

Given an object obj, the following two-line statements can be defined: var obj ={} //this is an object obj.isShiny = function () { console.log(this); return "you bet1"; }; These two lines can be combined into a one-line statement ...

What are the steps to incorporating the angular/material version 7.0.1 component into my project?

My journey with Angular and Google Material Design has been successful so far. I have been building my front-end app using the official documentation from https://angular.io/tutorial and https://material.angular.io/guides. While browsing through a similar ...

Issue: The 'loopback' module is not found in the NodeJS environment

I can't seem to solve the issue I'm experiencing. Error: Module 'loopback' not found These are the dependencies listed in my package.json file: "loopback": "^3.19.0", "loopback-boot": "^2.6.5", "loopback-component-explorer": "^6.0. ...

Displaying data from asynchronous functions in Vue.js

As a beginner in VueJs, my goal is to develop a basic application that fetches text from a Wikipedia page and displays it upon clicking a button. Code <script> const wiki = require('wikipedia'); export default { data() { sum:&q ...

Changing the URL within a Vue.js component without navigating away from the component

Just starting out with VueJs and working with wizaplace. I currently have a route set up as /breweries/:id, which uses the company's ID to fetch information from the wizaplace API. Within this data, there is a slug that I would like to display in the ...

Attempting to include a standard VAT rate of 5% on the invoice

/* The code format is correct and I don't see any issues on my end, I hope it works well for you. */ I need assistance in adding a fixed VAT (tax) rate of 5%. The tax amount should be displayed on the row, while the total tax should reflect the sum o ...

Issues arise with AngularJS showing images fetched from an API

Currently, I am facing an issue where I am trying to display images from a REST API but keep receiving a "error 403" message. Here is the link to my JSFiddle. Please take a look, as I have attempted using both <img src=""> and ng-src='', bu ...

Can PHP retrieve data when the form submit function is overridden with AJAX?

I have customized the .submit function of a form on this webpage. It is being loaded inside the #mainContent in an "index.php" and I want the Submit button to only replace this #mainContent. My goal is to retrieve data from this form and send it to a .php ...

What is the correct way to update a value in React context?

In my current setup, I have the ColorContext declared as follows: const ColorContext = React.createContext("") I am attempting to create a basic function that can alter the context value, which will be utilized as a global state variable in various other ...

Using a combination of jQuery and JavaScript to switch image tags between different classes

I'm really struggling with this issue and could use some guidance. Essentially, I have a code that should change the class of an img tag when a user clicks on a div element. Let's take a look at the HTML structure: <li><img src ...