Categories
Vuetify

Vuetify — Tables

Vuetify is a popular UI framework for Vue apps.

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

Tables

We can add a table with the v-simple-table component.

For example, we can add it by writing:

<template>
  <v-simple-table height="300px">
    <template v-slot:default>
      <thead>
        <tr>
          <th class="text-left">Name</th>
          <th class="text-left">Calories</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in desserts" :key="item.name">
          <td>{{ item.name }}</td>
          <td>{{ item.calories }}</td>
        </tr>
      </tbody>
    </template>
  </v-simple-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    desserts: [
      {
        name: "Yogurt",
        calories: 159,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
      },
      {
        name: "Eclair",
        calories: 262,
      }
    ],
  }),
};
</script>

We have the v-simple-table component with the default slot populated with the table rows.

We just use v-for to render the array entries into rows.

Fixed Header

The fixed-header with the height prop together let us fix the header to the top of the table.

For example, we can write:

<template>
  <v-simple-table height="150px" fixed-header>
    <template v-slot:default>
      <thead>
        <tr>
          <th class="text-left">Name</th>
          <th class="text-left">Calories</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in desserts" :key="item.name">
          <td>{{ item.name }}</td>
          <td>{{ item.calories }}</td>
        </tr>
      </tbody>
    </template>
  </v-simple-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    desserts: [
      {
        name: "Yogurt",
        calories: 159,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
      },
      {
        name: "Eclair",
        calories: 262,
      },
    ],
  }),
};
</script>

to make the table scrollable and keep the header always on top.

Dense Table

To add a dense version of the table, we can use the dense prop.

For example, we can write:

<template>
  <v-simple-table dense>
    <template v-slot:default>
      <thead>
        <tr>
          <th class="text-left">Name</th>
          <th class="text-left">Calories</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in desserts" :key="item.name">
          <td>{{ item.name }}</td>
          <td>{{ item.calories }}</td>
        </tr>
      </tbody>
    </template>
  </v-simple-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    desserts: [
      {
        name: "Yogurt",
        calories: 159,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
      },
      {
        name: "Eclair",
        calories: 262,
      },
    ],
  }),
};
</script>

The rows will be shorter than the default version.

Dark Theme

The dark prop lets us switch the table to the dark theme:

<template>
  <v-simple-table dark>
    <template v-slot:default>
      <thead>
        <tr>
          <th class="text-left">Name</th>
          <th class="text-left">Calories</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in desserts" :key="item.name">
          <td>{{ item.name }}</td>
          <td>{{ item.calories }}</td>
        </tr>
      </tbody>
    </template>
  </v-simple-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    desserts: [
      {
        name: "Yogurt",
        calories: 159,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
      },
      {
        name: "Eclair",
        calories: 262,
      },
    ],
  }),
};
</script>

Now the table will have a black background.

Conclusion

We can add a simple table with Vuetify’s v-simple-table component.

Categories
Vuetify

Vuetify — Table Footer and Slots

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.

Categories
Vuetify

Vuetify — Table Checkbox and Filtering

Vuetify is a popular UI framework for Vue apps.

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

Simple Checkbox

We can add a checkbox inside our table.

We can add the v-simple-checkbox component to add the checkbox.

For example, we can write:

<template>
  <div>
    <v-data-table :headers="headers" :items="desserts" class="elevation-1">
      <template v-slot:item.glutenfree="{ item }">
        <v-simple-checkbox v-model="item.glutenfree" disabled></v-simple-checkbox>
      </template>
    </v-data-table>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    desserts: [
      {
        name: "Frozen Yogurt",
        calories: 159,
        fat: 6.0,
        glutenfree: true,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
        glutenfree: false,
      },
      {
        name: "Eclair",
        calories: 262,
        fat: 16.0,
        glutenfree: false,
      },
    ],
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
      { text: "Gluten-Free", value: "glutenfree" },
    ],
  }),
};
</script>

to add a table with the v-data-table component.

The v-slot:item.glutenfree slot lets us add a checkbox to set the value of the glutenfree property of an entry.

Expandable Rows

The show-expand prop lets us render an expand icon on each row.

To add it, we can write:

<template>
  <v-data-table
    :headers="headers"
    :items="desserts"
    :expanded.sync="expanded"
    item-key="name"
    show-expand
    class="elevation-1"
  >
    <template v-slot:top>
      <v-toolbar flat>
        <v-toolbar-title>Expandable Table</v-toolbar-title>
      </v-toolbar>
    </template>
    <template v-slot:expanded-item="{ headers, item }">
      <td :colspan="headers.length">More info about {{ item.name }}</td>
    </template>
  </v-data-table>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    expanded: [],
    desserts: [
      {
        name: "Frozen Yogurt",
        calories: 159,
        fat: 6.0,
        glutenfree: true,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
        glutenfree: false,
      },
      {
        name: "Eclair",
        calories: 262,
        fat: 16.0,
        glutenfree: false,
      },
    ],
    headers: [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
      { text: "Gluten-Free", value: "glutenfree" },
    ],
  }),
};
</script>

We populate the expanded-item slot with our own items.

The expanded state to let us get and set which rows are expanded.

It’s used as the value of the expanded.sync prop so it can get and set the values.

Custom Filtering

We can add filtering to our table.

For instance, we can write:

<template>
  <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 (UPPER CASE ONLY)" 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>
</template>
<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      search: "",
      calories: "",
      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,
        },
      ],
    };
  },
  computed: {
    headers() {
      return [
        {
          text: "Dessert (100g serving)",
          align: "start",
          sortable: false,
          value: "name",
        },
        {
          text: "Calories",
          value: "calories",
          filter: (value) => {
            return !this.calories || value < parseInt(this.calories);
          },
        },
        { text: "Fat (g)", value: "fat" },
      ];
    },
  },
  methods: {
    filterOnlyCapsText(value, search, item) {
      return (
        value != null &&
        search != null &&
        typeof value === "string" &&
        value.toString().toLocaleUpperCase().indexOf(search) !== -1
      );
    },
  },
};
</script>

We have the filterOnlyCapsText method to let users search with upper case text.

Also, we have the header computed property so that we can filter the values properly when this.calories changes according to what we typed in.

Conclusion

We can add our own filtering logic to Vuetify tables.

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.