Vuetify table with column filter will not apply table filter to that column

936 Views Asked by At

I am using Vuetify 2.

I have custom filters for both a table and one of its columns, like this

https://codepen.io/hobbeschild/pen/zYJMjVY?editors=101

I cannot use the table search to find something in the column that has its own filter, because that column value is excluded from the table filter method. I feel that you should be able to search for "159" and get the first row. As well as using the calorie filter. Even if you extend the calorie filter to do a text search as well as its "less than" search, the false coming back from the table filter (because calories column is excluded) overrides the true from the column filter.

Any idea how to make the generic search read the calories column as well?

EDIT TO REPHRASE THE QUESTION

I want to put 159 in the Search box and see the row with 159 calories. I know I can use the Calorie filter for calories, and the Search box for names. I want to use the Search box for Calories (as well as the Calorie filter).

HTML:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-data-table
        :headers="headers"
        :items="desserts"
        item-key="name"
        class="elevation-1"
        :search="search"
        :custom-filter="filterOnlyCapsText"
      >
        <template v-slot:top>
          <v-text-field
            v-model="search"
            label="Search"
            class="mx-4"
          ></v-text-field>
        </template>
        <template v-slot:body.append>
          <tr>
            <td></td>
            <td>
              <v-text-field
                v-model="calories"
                type="number"
                label="Less than"
              ></v-text-field>
            </td>
            <td colspan="4"></td>
          </tr>
        </template>
      </v-data-table>
    </div>
  </v-app>
</div>

JS:

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data () {
    return {
      search: '',
      calories: '',
      desserts: [
        {
          name: 'Frozen Yogurt',
          calories: 159,
          fat: 6.0,
          carbs: 24,
          protein: 4.0,
          iron: 1,
        },
        {
          name: 'Ice cream sandwich',
          calories: 237,
          fat: 9.0,
          carbs: 37,
          protein: 4.3,
          iron: 1,
        },
        {
          name: 'Eclair',
          calories: 262,
          fat: 16.0,
          carbs: 23,
          protein: 6.0,
          iron: 7,
        },
        {
          name: 'Cupcake',
          calories: 305,
          fat: 3.7,
          carbs: 67,
          protein: 4.3,
          iron: 8,
        },
        {
          name: 'Gingerbread',
          calories: 356,
          fat: 16.0,
          carbs: 49,
          protein: 3.9,
          iron: 16,
        },
        {
          name: 'Jelly bean',
          calories: 375,
          fat: 0.0,
          carbs: 94,
          protein: 0.0,
          iron: 0,
        },
        {
          name: 'Lollipop',
          calories: 392,
          fat: 0.2,
          carbs: 98,
          protein: 0,
          iron: 2,
        },
        {
          name: 'Honeycomb',
          calories: 408,
          fat: 3.2,
          carbs: 87,
          protein: 6.5,
          iron: 45,
        },
        {
          name: 'Donut',
          calories: 452,
          fat: 25.0,
          carbs: 51,
          protein: 4.9,
          iron: 22,
        },
        {
          name: 'KitKat',
          calories: 518,
          fat: 26.0,
          carbs: 65,
          protein: 7,
          iron: 6,
        },
      ],
    }
  },
  computed: {
    headers () {
      return [
        {
          text: 'Dessert (100g serving)',
          align: 'start',
          sortable: false,
          value: 'name',
        },
        {
          text: 'Calories',
          value: 'calories',
          filter: value => {
            if (!this.calories) return true

            return value < parseInt(this.calories)
          },
        },
        { text: 'Fat (g)', value: 'fat' },
        { text: 'Carbs (g)', value: 'carbs' },
        { text: 'Protein (g)', value: 'protein' },
        { text: 'Iron (%)', value: 'iron' },
      ]
    },
  },
  methods: {
    filterOnlyCapsText (value, search, item) {
      return value != null &&
        search != null &&
        value.toString().toLowerCase().indexOf(search) !== -1
    },
  },
})
1

There are 1 best solutions below

1
IVO GELOV On

This is the piece of code in Vuetify which performs the filtration:

  return items.filter(item => {
    // Headers with custom filters are evaluated whether or not a search term has been provided.
    // We need to match every filter to be included in the results.
    const matchesColumnFilters = headersWithCustomFilters.every(filterFn(item, search, defaultFilter))

    // Headers without custom filters are only filtered by the `search` property if it is defined.
    // We only need a single column to match the search term to be included in the results.
    const matchesSearchTerm = !search || headersWithoutCustomFilters.some(filterFn(item, search, customFilter))

    return matchesColumnFilters && matchesSearchTerm
  })

As you can see - the result is a logical AND from both type of columns (those with custom filter and those without a filter). customFilter will be your filterOnlyCapsText function while defaultFilter is implemented like this

export function defaultFilter (value, search, item) {
  return value != null &&
    search != null &&
    typeof value !== 'boolean' &&
    value.toString().toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) !== -1
}

A non-ideal solution seems to be to dynamically remove (undefine) the column filter from the column definition when the search term is non-empty.
Of course this will completely defeat the Calories filter - but at least it will obey the Search term.

A more complete solution would be to use your own filtration instead of the VDataTable filtration.