Categories
Vuetify

Vuetify — Subheaders and Data Iterators

Vuetify is a popular UI framework for Vue apps.

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

Subheaders with Social Media

We can add subheaders with social media icons below it.

For example, we can write:

<template>
  <v-card flat tile>
    <v-container v-for="type in types" :key="type" class="grey lighten-4" fluid>
      <v-subheader>{{ type }}</v-subheader>

      <v-row>
        <v-spacer></v-spacer>
        <v-col v-for="card in cards" :key="card" cols="12" sm="6" md="4">
          <v-card>
            <v-img
              :src="`https://picsum.photos/200/300?image=${Math.floor(Math.random()*1000)}`"
              height="300px"
            >
              <span class="headline white--text pl-4 pt-4" v-text="card.title"></span>
            </v-img>

            <v-card-actions class="white justify-center">
              <v-btn
                v-for="(social, i) in socials"
                :key="i"
                :color="social.color"
                class="white--text"
                fab
                icon
                small
              >
                <v-icon>{{ social.icon }}</v-icon>
              </v-btn>
            </v-card-actions>
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </v-card>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    types: ["Places to Be", "Places to See"],
    cards: ["Good", "Best", "Finest"],
    socials: [
      {
        icon: "mdi-facebook",
        color: "indigo",
      },
      {
        icon: "mdi-linkedin",
        color: "cyan darken-1",
      },
      {
        icon: "mdi-instagram",
        color: "red lighten-3",
      },
    ],
  }),
};
</script>

We have the v-subheader component above the v-row .

The subheader will be aligned with the image grid.

The social media icons are in the v-card-actions component.

Table Data Iterators

We can display table data with the v-data-iteratir component.

For example, we can write:

<template>
  <v-container fluid>
    <v-data-iterator :items="items" :items-per-page.sync="itemsPerPage" hide-default-footer>
      <template v-slot:header>
        <v-toolbar class="mb-2" color="indigo darken-5" dark flat>
          <v-toolbar-title>This is a header</v-toolbar-title>
        </v-toolbar>
      </template>

<template v-slot:default="props">
        <v-row>
          <v-col v-for="item in props.items" :key="item.name" cols="12" sm="6" md="4" lg="3">
            <v-card>
              <v-card-title class="subheading font-weight-bold">{{ item.name }}</v-card-title>

<v-divider></v-divider>

<v-list dense>
                <v-list-item>
                  <v-list-item-content>Calories:</v-list-item-content>
                  <v-list-item-content class="align-end">{{ item.calories }}</v-list-item-content>
                </v-list-item>

<v-list-item>
                  <v-list-item-content>Fat:</v-list-item-content>
                  <v-list-item-content class="align-end">{{ item.fat }}</v-list-item-content>
                </v-list-item>
              </v-list>
            </v-card>
          </v-col>
        </v-row>
      </template>

<template v-slot:footer>
        <v-toolbar class="mt-2" color="indigo" dark flat>
          <v-toolbar-title class="subheading">This is a footer</v-toolbar-title>
        </v-toolbar>
      </template>
    </v-data-iterator>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    itemsPerPage: 4,
    items: [
      {
        name: "Yogurt",
        calories: 159,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        name: "Donuts",
        calories: 262,
        fat: 16.0,
      },
      {
        name: "Cupcake",
        calories: 305,
        fat: 3.7,
      },
    ],
  }),
};
</script>

to display data in a v-data-iterator component.

The data will be displayed in cards which has a list with the column data.

The list is in the v-list component with the v-list-item and v-list-item-content in the list.

The v-card-title has the card title.

Also, we populated the header and footer slots with the header and footer content.

Conclusion

We can subheaders for various content.

Also, we can use the v-data-iterator component to render an array of data to the screen.

Categories
Vuetify

Vuetify — Edit Table Content

Vuetify is a popular UI framework for Vue apps.

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

Editable Table Columns

We can add the v-edit-dialog component to make some columns editable.

For example, we can write:

<template>
  <div>
    <v-data-table :headers="headers" :items="desserts">
      <template v-slot:item.name="props">
        <v-edit-dialog
          :return-value.sync="props.item.name"
          @save="save"
          @cancel="cancel"
          @open="open"
          @close="close"
        >
          {{ props.item.name }}
          <template v-slot:input>
            <v-text-field
              v-model="props.item.name"
              :rules="[max25chars]"
              label="Edit"
              single-line
              counter
            ></v-text-field>
          </template>
        </v-edit-dialog>
      </template>
    </v-data-table>

    <v-snackbar v-model="snack" :timeout="3000" :color="snackColor">
      {{ snackText }}
      <template v-slot:action="{ attrs }">
        <v-btn v-bind="attrs" text @click="snack = false">Close</v-btn>
      </template>
    </v-snackbar>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      snack: false,
      snackColor: "",
      snackText: "",
      max25chars: (v) => v.length <= 25 || "Input too long!",
      pagination: {},
      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: 159,
          fat: 6.0,
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
        },
      ],
    };
  },
  methods: {
    save() {
      this.snack = true;
      this.snackColor = "success";
      this.snackText = "Data saved";
    },
    cancel() {
      this.snack = true;
      this.snackColor = "error";
      this.snackText = "Canceled";
    },
    open() {
      this.snack = true;
      this.snackColor = "info";
      this.snackText = "Dialog opened";
    },
    close() {
      console.log("Dialog closed");
    },
  },
};
</script>

to make the name column editable.

We have the v-edit-dialog that has the input slot populated with the v-text-field component to let us edit the text when we click on the cell.

The rules prop lets us set the validation rules.

We either return undefined or an error message if there is one.

CRUD Actions

We can add a dialog box that opens when we click on a button in a table row.

To do that, we write:

<template>
  <v-data-table :headers="headers" :items="desserts" sort-by="calories" class="elevation-1">
    <template v-slot:top>
      <v-toolbar flat color="white">
        <v-toolbar-title>My CRUD</v-toolbar-title>
        <v-divider class="mx-4" inset vertical></v-divider>
        <v-spacer></v-spacer>
        <v-dialog v-model="dialog" max-width="500px">
          <template v-slot:activator="{ on, attrs }">
            <v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">New Item</v-btn>
          </template>
          <v-card>
            <v-card-title>
              <span class="headline">{{ formTitle }}</span>
            </v-card-title>

<v-card-text>
              <v-container>
                <v-row>
                  <v-col cols="12" sm="6" md="4">
                    <v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
                  </v-col>
                  <v-col cols="12" sm="6" md="4">
                    <v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
                  </v-col>
                  <v-col cols="12" sm="6" md="4">
                    <v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
                  </v-col>
                </v-row>
              </v-container>
            </v-card-text>

            <v-card-actions>
              <v-spacer></v-spacer>
              <v-btn color="blue darken-1" text @click="close">Cancel</v-btn>
              <v-btn color="blue darken-1" text @click="save">Save</v-btn>
            </v-card-actions>
          </v-card>
        </v-dialog>
      </v-toolbar>
    </template>
    <template v-slot:item.actions="{ item }">
      <v-icon small class="mr-2" @click="editItem(item)">mdi-pencil</v-icon>
      <v-icon small @click="deleteItem(item)">mdi-delete</v-icon>
    </template>
    <template v-slot:no-data>
      <v-btn color="primary" @click="initialize">Reset</v-btn>
    </template>
  </v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
      { text: "Actions", value: "actions", sortable: false },
    ],
    desserts: [],
    editedIndex: -1,
    editedItem: {
      name: "",
      calories: 0,
      fat: 0,
      carbs: 0,
      protein: 0,
    },
    defaultItem: {
      name: "",
      calories: 0,
      fat: 0,
      carbs: 0,
      protein: 0,
    },
  }),

  computed: {
    formTitle() {
      return this.editedIndex === -1 ? "New Item" : "Edit Item";
    },
  },

  watch: {
    dialog(val) {
      val || this.close();
    },
  },

  created() {
    this.initialize();
  },

  methods: {
    initialize() {
      this.desserts = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
        },
      ];
    },

    editItem(item) {
      this.editedIndex = this.desserts.indexOf(item);
      this.editedItem = Object.assign({}, item);
      this.dialog = true;
    },

    deleteItem(item) {
      const index = this.desserts.indexOf(item);
      confirm("Are you sure you want to delete this item?") &&
        this.desserts.splice(index, 1);
    },

    close() {
      this.dialog = false;
      this.$nextTick(() => {
        this.editedItem = Object.assign({}, this.defaultItem);
        this.editedIndex = -1;
      });
    },

    save() {
      if (this.editedIndex > -1) {
        Object.assign(this.desserts[this.editedIndex], this.editedItem);
      } else {
        this.desserts.push(this.editedItem);
      }
      this.close();
    },
  },
};
</script>

We populate the top slot with our dialog box.

The dialog state lets us control when it’s open.

When we click on the pencil button, the editItem method is run.

this.editedIndex is set and the this.editedItem object is set so that the form will be populated with the values.

Also, when we click on the trash can button, we run the deleteItem method.

We show the confirm dialog box and remove the item with the given index.

Conclusion

We can add and remove items from a table with some methods.

Also, we can edit column content by clicking on a cell with the v-edit-dialog component.

Categories
Vuetify

Vuetify — Data Tables Customization

Vuetify is a popular UI framework for Vue apps.

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

Data Tables Search

We can add a search prop to allow us to filter data in a data table.

For example, we can write:

<template>
  <v-card>
    <v-card-title>
      Nutrition
      <v-spacer></v-spacer>
      <v-text-field
        v-model="search"
        append-icon="mdi-magnify"
        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",
        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 add the v-text-field that binds to the search state.

We pass search into the search prop and we can search any column we like.

Remove Default Header and Footer

The hide-default-header and hide-default-footer props let us remove the default header and footer.

For example, we can write:

<template>
  <v-data-table
    :headers="headers"
    :items="desserts"
    hide-default-header
    hide-default-footer
    class="elevation-1"
  ></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>

Loading State

We can add the loading prop to indicate that the table is loading.

Also, we can display a message when the table is loading.

For example, we can write:

<template>
  <v-data-table item-key="name" class="elevation-1" loading loading-text="Loading... Please wait"></v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

The loading-text lets us set the loading text to display.

Dense

We can add the dense prop to make the rows shorter:

<template>
  <v-data-table :headers="headers" :items="desserts" dense class="elevation-1"></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>

Now the rows are shorter.

Conclusion

We can customize our table with search, sorting, and pagination with Vuetify.

Categories
Vuetify

Vuetify — Data Tables

Vuetify is a popular UI framework for Vue apps.

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

Data Tables

The v-data-table component is used to display tabular data.

It includes features like sorting, searching, pagination, editing, and row selection.

For example, we can write:

<template>
  <v-data-table
    v-model="selected"
    :headers="headers"
    :items="desserts"
    :single-select="singleSelect"
    item-key="name"
    show-select
    class="elevation-1"
  >
    <template v-slot:top>
      <v-subheader>Dessert</v-subheader>
    </template>
  </v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    singleSelect: false,
    selected: [],
    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: 159,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        name: "Eclair",
        calories: 262,
        fat: 16.0,
      },
    ],
  }),
};
</script>

We have the v-data-table component to add our table.

The v-model has the selected rows.

items have the items to display.

single-select lets us toggle single select.

The headers prop have the headers.

The value is an array with some objects.

The text property has the column heading text.

align has the text alignment.

sortable lets us set whether it’s sortable or not.

value has the property name of the entry to display.

Pagination is automatically included.

Grouped Rows

We can group different rows together with the group-by and group-desc props.

For example, we can write:

<template>
  <v-data-table
    v-model="selected"
    :headers="headers"
    :items="desserts"
    :single-select="singleSelect"
    item-key="name"
    show-group-by
    group-by="category"
    class="elevation-1"
  >
    <template v-slot:top>
      <v-subheader>Dessert</v-subheader>
    </template>
  </v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        value: "name",
        groupable: false,
      },
      { text: "Category", value: "category", align: "right" },
      { text: "Dairy", value: "dairy", align: "right" },
    ],
    desserts: [
      {
        name: "Frozen Yogurt",
        category: "Ice cream",
        dairy: "Yes",
      },
      {
        name: "Ice cream sandwich",
        category: "Ice cream",
        dairy: "Yes",
      },
      {
        name: "Eclair",
        category: "Cookie",
        dairy: "Yes",
      },
    ],
  }),
};
</script>

We have the v-data-table component to display items in a group.

The group-by prop takes the string with the property key to group by.

The show-group-by prop lets us add the group by button to let us group by the properties.

Sort on Multiple Columns

We can sort a table by multiple columns.

For example, we can write:

<template>
  <v-data-table
    :headers="headers"
    :items="desserts"
    :sort-by="['calories', 'fat']"
    :sort-desc="[false, true]"
    multi-sort
    class="elevation-1"
  ></v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    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 added the sort-by prop with an array of the property names to sort by.

sort-desc lets us set whether to sort the column in the position in sort-by in descending order or not.

Conclusion

We can add tables that can be sorted and grouped with Vuetify.

Categories
Vuetify

Vuetify — Data Iterators

Vuetify is a popular UI framework for Vue apps.

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

Data Iterator — Expanding Items

We can expand and collapse items with the v-data-iterator component.

For example, we can write:

<template>
  <v-container fluid>
    <v-data-iterator
      :items="items"
      item-key="name"
      :items-per-page="4"
      :single-expand="expand"
      hide-default-footer
    >
      <template v-slot:default="{ items, isExpanded, expand }">
        <v-row>
          <v-col v-for="item in items" :key="item.name" cols="12" sm="6" md="4" lg="3">
            <v-card>
              <v-card-title>
                <h4>{{ item.name }}</h4>
              </v-card-title>
              <v-switch
                :input-value="isExpanded(item)"
                :label="isExpanded(item) ? 'Expanded' : 'Closed'"
                class="pl-4 mt-0"
                @change="(v) => expand(item, v)"
              ></v-switch>
              <v-divider></v-divider>
              <v-list v-if="isExpanded(item)" dense>
                <v-list-item>
                  <v-list-item-content>Calories:</v-list-item-content>
                  <v-list-item-content class="align-end">{{ item.calories }}</v-list-item-content>
                </v-list-item>
                <v-list-item>
                  <v-list-item-content>Fat:</v-list-item-content>
                  <v-list-item-content class="align-end">{{ item.fat }}</v-list-item-content>
                </v-list-item>
              </v-list>
            </v-card>
          </v-col>
        </v-row>
      </template>
    </v-data-iterator>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    itemsPerPage: 4,
    items: [
      {
        name: "Yogurt",
        calories: 159,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        name: "Donuts",
        calories: 262,
        fat: 16.0,
      },
      {
        name: "Cupcake",
        calories: 305,
        fat: 3.7,
      },
    ],
  }),
};
</script>

We get the isExpanded function from the default slot to style our entries differently if it’s expanded or not.

The expand function from the slot lets us expand or collapse the entry if we click on the v-switch .

Filter Items

We can order, filter, and paginate items.

For example, we can write:

<template>
  <v-container fluid>
    <v-data-iterator
      :items="items"
      :items-per-page.sync="itemsPerPage"
      :page="page"
      :search="search"
      :sort-by="sortBy.toLowerCase()"
      :sort-desc="sortDesc"
      hide-default-footer
    >
      <template v-slot:header>
        <v-toolbar dark color="blue darken-3" class="mb-1">
          <v-text-field
            v-model="search"
            clearable
            flat
            solo-inverted
            hide-details
            prepend-inner-icon="search"
            label="Search"
          ></v-text-field>
          <template v-if="$vuetify.breakpoint.mdAndUp">
            <v-spacer></v-spacer>
            <v-select
              v-model="sortBy"
              flat
              solo-inverted
              hide-details
              :items="keys"
              prepend-inner-icon="search"
              label="Sort by"
            ></v-select>
            <v-spacer></v-spacer>
            <v-btn-toggle v-model="sortDesc" mandatory>
              <v-btn large depressed color="blue" :value="false">
                <v-icon>mdi-arrow-up</v-icon>
              </v-btn>
              <v-btn large depressed color="blue" :value="true">
                <v-icon>mdi-arrow-down</v-icon>
              </v-btn>
            </v-btn-toggle>
          </template>
        </v-toolbar>
      </template>

      <template v-slot:default="props">
        <v-row>
          <v-col v-for="item in props.items" :key="item.name" cols="12" sm="6" md="4" lg="3">
            <v-card>
              <v-card-title class="subheading font-weight-bold">{{ item.name }}</v-card-title>

              <v-divider></v-divider>

              <v-list dense>
                <v-list-item v-for="(key, index) in filteredKeys" :key="index">
                  <v-list-item-content :class="{ 'blue--text': sortBy === key }">{{ key }}:</v-list-item-content>
                  <v-list-item-content
                    class="align-end"
                    :class="{ 'blue--text': sortBy === key }"
                  >{{ item[key.toLowerCase()] }}</v-list-item-content>
                </v-list-item>
              </v-list>
            </v-card>
          </v-col>
        </v-row>
      </template>

      <template v-slot:footer>
        <v-row class="mt-2" align="center" justify="center">
          <span class="grey--text">Items per page</span>
          <v-menu offset-y>
            <template v-slot:activator="{ on, attrs }">
              <v-btn dark text color="primary" class="ml-2" v-bind="attrs" v-on="on">
                {{ itemsPerPage }}
                <v-icon>mdi-chevron-down</v-icon>
              </v-btn>
            </template>
            <v-list>
              <v-list-item
                v-for="(number, index) in itemsPerPageArray"
                :key="index"
                @click="updateItemsPerPage(number)"
              >
                <v-list-item-title>{{ number }}</v-list-item-title>
              </v-list-item>
            </v-list>
          </v-menu>

          <v-spacer></v-spacer>

          <span class="mr-4 grey--text">Page {{ page }} of {{ numberOfPages }}</span>
          <v-btn fab dark color="blue darken-3" class="mr-1" @click="formerPage">
            <v-icon>mdi-chevron-left</v-icon>
          </v-btn>
          <v-btn fab dark color="blue darken-3" class="ml-1" @click="nextPage">
            <v-icon>mdi-chevron-right</v-icon>
          </v-btn>
        </v-row>
      </template>
    </v-data-iterator>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    itemsPerPageArray: [1, 2],
    search: "",
    filter: {},
    sortDesc: false,
    page: 1,
    itemsPerPage: 1,
    sortBy: "name",
    keys: ["Name", "Calories", "Fat"],
    items: [
      {
        name: "Yogurt",
        calories: 159,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        name: "Donuts",
        calories: 262,
        fat: 16.0,
      },
      {
        name: "Cupcake",
        calories: 305,
        fat: 3.7,
      },
    ],
  }),
  computed: {
    numberOfPages() {
      return Math.ceil(this.items.length / this.itemsPerPage);
    },
    filteredKeys() {
      return this.keys.filter((key) => key !== `Name`);
    },
  },
  methods: {
    nextPage() {
      if (this.page + 1 <= this.numberOfPages) this.page += 1;
    },
    formerPage() {
      if (this.page - 1 >= 1) this.page -= 1;
    },
    updateItemsPerPage(number) {
      this.itemsPerPage = number;
    },
  },
};
</script>

We have the text field to search for items in the header slot.

The search state will be set if we type something into the text field in the header.

The v-btn-toggle component lets us toggle the sorting between forward and reverse order.

When we click on the buttons, we toggle the sortDesc state.

Also, we have a dropdown that sets the sortBy state to let us sort items by the given key automatically.

All the states are used as prop values in the v-data-iterator component.

Then all the sorting, pagination, and search logic will be taken care of by the v-data-iterator .

We also have a v-select to let us change the number of items per page with the updateItemsPerPage method.

The content is populated in the default slot.

The props.items property has the items that we want to render into cards.

Conclusion

We can add sorting, pagination, and searching with the v-data-iterator component with a few props.