Search code examples
vue.jsvuetify.jsv-data-table

Sort and Filtering for Custom Header in Vuetify 2


I'm new to Vuetify and am struggling to add the default filter and sort capabilities back into a table with a custom header.

The HTML:

<v-data-table
  :headers="headers"
  :items="items"
  item-key="client"
  :footer-props="footerProps"
  :search="search"
  class="text-body-1"
>
  <template #header="{ props }">
    <thead class="v-data-table-header">
    <tr>
      <th
        v-for="header in props.headers"
        :key="header.value"
        :class="{
                    'grey white--text text-body-1': true,
                    'sortable': header.sortable,
                    'filter': header.filterable,
                  }"
        scope="col"
      >
        {{ header.text }}
      </th>
    </tr>
    </thead>
  </template>

The data structure for the headers I am working off of looks as follow:

headers: [
    new TableHeader('Number', 'index', false, false),
    new TableHeader('EMP Code', 'empCode', false, false),
]

and the TableHeader:

export default function (header, prop, sortable = true, filterable = false, align = 'start') {
  this.text = header
  this.value = prop
  this.sortable = sortable
  this.filterable = filterable
  this.align = align
}

With all this, the table header looks exactly the way it is supposed to but I can no longer click on the header and sort it.

Does anyone know how to add the default filter and sort back or maybe just a different way to style the header that keeps the default header functionalities with custom styling. Nothing changes on the header except for the style.

(I've seen lots of questions similar to this one but none of them really answer it in a way I can replicate :')

I'm expecting my header to have custom styling with the default functionalities (sort, filter, etc.) but I am getting a custom look with no default functionalities (sort, filter, etc.)


Solution

  • The #header slot receives an object on as slot prop, which contains callbacks:

      on: {
        sort: (value: string) => void,
        group: (value: string) => void,
        toggle-select-all: (value: boolean) => void
      }
    

    Pass header.value to the on.sort function to trigger sorting:

    <template #header="{ props, on }">
      <tr>
        <th
          v-for="header in props.headers"
          ...
          @click="on.sort(header.value)"
        >
          ...
    

    It will go trough the same sort order cycle as on regular headers (off -> asc -> desc -> off).

    Here it is in a snippet:

    new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      template: `
        <v-app>
          <v-main>
            <v-container>
              <v-data-table
                :items="items"
                :headers="headers"
              >
                <template #header="{props, on: {sort}}">
                  <tr>
                    <th
                      v-for="header in props.headers"
                      :key="header.value"
                      @click="sort(header.value)"
                    >
                      Le {{header.value}} <span
                        v-if="props.options.sortBy[0] === header.value"
                      >{{ props.options.sortDesc[0] ? 'desc' : 'asc'}}
                      </span>
                    </th>
                  </tr>
                </template>
              </v-data-table>
            </v-container>
          </v-main>
        </v-app>
      `,
      data(){
        return {
          headers: [{text: 'Number', value: 'number'}, {text: 'Letter', value: 'letter'}],
          items: Array.from({length: 5}, (_,i) => ({number: i+1, letter: String.fromCharCode(90-i)})),
        }
      },
      methods: {
        log(...x){console.log(...x)}
      }
    })
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
    
    <div id="app"></div>
    
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>