Categories
Vuetify

Vuetify — Table Footer and Slots

Spread the love

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Footer Props

We can change the v-data-table ‘s footer-props prop to change the footer.

For example, we can write:

<template>
  <v-data-table
    :headers="headers"
    :items="desserts"
    :items-per-page="5"
    item-key="name"
    class="elevation-1"
    :footer-props="{
      showFirstLastPage: true,
      firstIcon: 'mdi-arrow-collapse-left',
      lastIcon: 'mdi-arrow-collapse-right',
      prevIcon: 'mdi-minus',
      nextIcon: 'mdi-plus'
    }"
  ></v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    search: "",
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
    ],
    desserts: [
      {
        name: "Frozen Yogurt",
        calories: 200,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 200,
        fat: 9.0,
      },
      {
        name: "Eclair",
        calories: 300,
        fat: 16.0,
      },
    ],
  }),
};
</script>

We have an object with the firstIcon , lastIcon , prevIcon , and nextIcon properties to change the navigation buttons for the table.

showFirstLastPage lets us show the buttons to go to the first and last page.

Filterable Columns

We can set some columns to be filterable with the filterable property.

For example, we can write:

<template>
  <v-card>
    <v-card-title>
      <v-text-field v-model="search" append-icon="search" label="Search" single-line hide-details></v-text-field>
    </v-card-title>
    <v-data-table :headers="headers" :items="desserts" :search="search"></v-data-table>
  </v-card>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    search: "",
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        filterable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
    ],
    desserts: [
      {
        name: "Frozen Yogurt",
        calories: 200,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 200,
        fat: 9.0,
      },
      {
        name: "Eclair",
        calories: 300,
        fat: 16.0,
      },
    ],
  }),
};
</script>

to disable filtering on the first column.

We added the filterable property into the object in the headers array.

Slots

We can populate various slots with our content.

For example, we can write:

<template>
  <div>
    <v-select v-model="enabled" :items="slots" label="Slot" clearable></v-select>
    <v-data-table
      :headers="headers"
      :items="items"
      :search="search"
      :hide-default-header="hideHeaders"
      :show-select="showSelect"
      :loading="isLoading"
      hide-default-footer
      item-key="name"
      class="elevation-1"
    >
      <template v-if="isEnabled('top')" v-slot:top>
        <div>This is content above the actual table</div>
      </template>

      <template
        v-show="isEnabled('header.data-table-select')"
        v-slot:header.data-table-select="{ on, props }"
      >
        <v-simple-checkbox color="purple" v-bind="props" v-on="on"></v-simple-checkbox>
      </template>

<template v-if="isEnabled('header')" v-slot:header="{ props: { headers } }">
        <thead>
          <tr>
            <th :colspan="headers.length">This is a header</th>
          </tr>
        </thead>
      </template>

<template v-show="isEnabled('progress')" v-slot:progress>
        <v-progress-linear color="purple" :height="10" indeterminate></v-progress-linear>
      </template>

      <template
        v-show="isEnabled('item.data-table-select')"
        v-slot:item.data-table-select="{ isSelected, select }"
      >
        <v-simple-checkbox color="green" :value="isSelected" @input="select($event)"></v-simple-checkbox>
      </template>

      <template
        v-show="isEnabled('item.<name>')"
        v-slot:item.name="{ item }"
      >{{ item.name.toUpperCase() }}</template>

<template v-show="isEnabled('body.prepend')" v-slot:body.prepend="{ headers }">
        <tr>
          <td :colspan="headers.length">This is a prepended row</td>
        </tr>
      </template>

      <template v-show="isEnabled('body')" v-slot:body="{ items }">
        <tbody>
          <tr v-for="item in items" :key="item.name">
            <td>{{ item.name }}</td>
          </tr>
        </tbody>
      </template>

      <template v-show="isEnabled('no-data')" v-slot:no-data>NO DATA HERE!</template>

      <template v-show="isEnabled('no-results')" v-slot:no-results>NO RESULTS HERE!</template>

      <template v-show="isEnabled('body.append')" v-slot:body.append="{ headers }">
        <tr>
          <td :colspan="headers.length">This is an appended row</td>
        </tr>
      </template>

      <template v-show="isEnabled('footer')" v-slot:footer>
        <div>This is a footer</div>
      </template>
    </v-data-table>
  </div>
</template>
<script>
const desserts = [
  {
    name: "Frozen Yogurt",
    calories: 200,
    fat: 6.0,
  },
  {
    name: "Ice cream sandwich",
    calories: 200,
    fat: 9.0,
  },
  {
    name: "Eclair",
    calories: 300,
    fat: 16.0,
  },
];

export default {
  name: "HelloWorld",
  data: () => ({
    enabled: null,
    search: null,
    slots: [
      "body",
      "body.append",
      "body.prepend",
      "footer",
      "header.data-table-select",
      "header",
      "progress",
      "item.data-table-select",
      "item.<name>",
      "no-data",
      "no-results",
      "top",
    ],
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
    ],
    items: desserts,
  }),
  computed: {
    showSelect() {
      return (
        this.isEnabled("header.data-table-select") ||
        this.isEnabled("item.data-table-select")
      );
    },
    hideHeaders() {
      return !this.showSelect;
    },
    isLoading() {
      return this.isEnabled("progress");
    },
  },

  watch: {
    enabled(slot) {
      if (slot === "no-data") {
        this.items = [];
      } else if (slot === "no-results") {
        this.search = "...";
      } else {
        this.search = null;
        this.items = desserts;
      }
    },
  },

  methods: {
    isEnabled(slot) {
      return this.enabled === slot;
    },
  },
};
</script>

We add the template elements with the slots.

There are many slots for the body, showing data when there’s no data or no results, a footer, and more.

The dropdown sets which one is enabled and we use v-show to show the enabled ones.

Conclusion

We can populate various slots with our own content.

Footer icons can also be changed.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *