2
votes

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
Are you sur that it is not call. To me if(newVal != oldVal) is wrong. It is not the correct way to compare object in js.mathk

1 Answers

0
votes

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