Vuetify datatable watch does not trigger

2.8k views Asked by At

The function "change sort" executed on header item click does not trigger watch, which fetches updated data from server. On the other hand, if I try to execute fetchRecords method at the end of changeSort method, watch gets triggered multiple times

I need to use Vuetify datatables with server side pagination and sorting, and templates for header and items. I implemented code similarly to Vuetify examples: "Paginate and sort server-side" and "Slot: items and headers" from documentation https://vuetifyjs.com/en/components/data-tables#api.

<template>
  <v-card class="table-container">
    <v-card-title>
      <v-text-field
        v-model="searchField"
        @blur="fetchRecords()"
        @keyup.enter="fetchRecords()"
      />
    </v-card-title>
    <v-data-table
      :headers="headers"
      :items="applications.applications"
      :loading="paginationLoading"
      :pagination.sync="pagination"
      :total-items="pagination.totalItems"
      no-data-text="No results"
    >
      <template v-slot:headers="props">
        <tr>
          <th colspan="4">Dane kandydata</th>
          <th colspan="4">Dane polecającego</th>
          <th colspan="2">Inne</th>
        </tr>
        <tr>
          <th
            v-for="header in props.headers"
            :key="header.value"
            :class="['column', 
              header.sortable !== false ? 'sortable' : '' ,
              pagination.descending ? 'desc' : 'asc', 
              header.value === pagination.sortBy ? 'active' : '']"
            @click="changeSort(header.value, header.sortable)"
          >
            {{ header.text }}
            <v-icon v-if="header.sortable !== false" small>fas fa-sort-up</v-icon>
          </th>
        </tr>
      </template>
      <template v-slot:items="props">
        <td>{{ props.item.candidateName }}</td>
        <td>{{ props.item.candidateSurname }}</td>
        <td>{{ props.item.candidateEmail }}</td>
        <td>{{ props.item.candidatePhone }}</td>
        <td>{{ props.item.referrerName }}</td>
        <td>{{ props.item.referrerSurname }}</td>
        <td>{{ props.item.referrerEmail }}</td>
        <td>{{ props.item.referrerPhone }}</td>
        <td class="text-md-center">
          <div>
            <v-icon>check_circle_outline</v-icon>
          </div>
        </td>
        <td class="text-md-center">
          <div>
            <v-icon>check_circle_outline</v-icon>
          </div>
        </td>
      </template>
      <v-alert v-slot:no-results :value="true" color="error" icon="warning">
        Your search for "{{ searchField }}" found no results.
      </v-alert>
    </v-data-table>
  </v-card>
</template>

<script>
  import { mapState, mapActions } from 'vuex';

  export default {
    data () {
      return {
        announcementId: '',
        announcementTitle: '',
        searchField: '',
        headers: [
          { text: 'Imię', value: 'candidateName' },
          { text: 'Nazwisko', value: 'candidateSurname' },
          { text: 'Email', value: 'candidateEmail', sortable: false },
          { text: 'Telefon', value: 'candidatePhone' },
          { text: 'Imię', value: 'referrerName' },
          { text: 'Nazwisko', value: 'referrerSurname' },
          { text: 'Email', value: 'referrerEmail', sortable: false },
          { text: 'Telefon', value: 'referrerPhone' },
          { text: 'Status', value: 'processed' },
          { text: 'Akcje', value: 'actions', sortable: false },
        ],
        pagination: {
          page: 1,
          rowsPerPage: 10,
          totalItems: 10,
          sortBy: '',
          descending: false,
        },
        paginationLoading: false
      }
    },
    computed: {
      ...mapState([
        'applications',
      ])
    },
    watch: {
      pagination: {
        handler(newVal, oldVal){
          if(newVal != oldVal){
            this.fetchRecords()
          }
        },
        deep: true
      }
    },
    methods: {
      ...mapActions([
        'getApplications',
      ]),
      fetchRecords () {
        this.paginationLoading = true
        this.getApplications({
          announcementId: this.announcementId,
          pagination: this.pagination,
          search: this.searchField
        })
        .then(
          response => {
            this.paginationLoading = false
            if(this.pagination.totalItems != response.totalItems) this.pagination.totalItems = response.totalItems
          }
        )
        .catch(
          err => {
            console.log(err);
          }
        )
    },
    changeSort (columnValue, sortable) {
      if(sortable === false){
        return
      }
      if (this.pagination.sortBy === columnValue) {
        this.pagination.descending = !this.pagination.descending
        console.log(this.pagination.descending);
      } else {
        this.pagination.sortBy = columnValue
        this.pagination.descending = false
      }
    },
    editTableItem(item){
      console.log(item);
    },
    onClearSearch() {
      this.searchField = ''
      this.fetchRecords()
    },
  }
}
</script>
1

There are 1 answers

1
ittus On

You should create new object when changing pagination object. Here I use ES6 syntax:

changeSort (columnValue, sortable) {
    if(sortable === false){
      return
    }
    if (this.pagination.sortBy === columnValue) {
      this.pagination = {
        ...this.pagination,
        descending: !this.pagination.descending
      }
      console.log(this.pagination.descending);
    } else {
      this.pagination = {
        ...this.pagination,
        sortBy: columnValue,
        descending: false
      }
    }
}