Home » vue » Adding the column name in the table and links in vue

Adding the column name in the table and links in vue

Posted by: admin November 26, 2021 Leave a comment

Questions:

I am beginner web developer. I make my first crud in Laravel 8 and Vue.
I use this component t in my project: https://www.npmjs.com/package/vuejs-datatable

I have this code:

DataTable.vue:

<template>
  <div>
    <div class="row mb-3">
      <div class="col-3">
        <div class="input-group">
          <input
            v-model="search"
            class="form-control"
            placeholder="Szukaj..."
            type="text"
          >
          <div class="input-group-append">
            <button class="btn btn-primary" type="button" @click.prevent="handleSearch">
              <font-awesome-icon icon="fas fa-search" />
            </button>
          </div>
        </div>
      </div>

      <div class="col-2">
        <div class="input-group">
          <label for="pageOption" class="mt-2 mr-2">Na stronie</label>
          <select class="form-control" v-model="perPage" @change="handlePerPage" id="pageOption">
            <option v-for="page in pageOptions" :key="page" :value="page">{{ page }}</option>
          </select>
        </div>
      </div>
    </div>


    <table class="table table-hover">
      <thead>
      <tr>
        <th class="table-head">#</th>
        <th v-for="column in columns" :key="column" @click="sortByColumn(column)"
            class="table-head">
          {{ column | columnHead }}
          <span v-if="column === sortedColumn">
                            <font-awesome-icon v-if="order === 'asc' "  icon="fas fa-angle-up" />
                            <font-awesome-icon v-else icon="fas fa-angle-down" />
            </span>
        </th>
      </tr>
      </thead>
      <tbody>
      <tr class="" v-if="tableData.length === 0">
        <td class="lead text-center" :colspan="columns.length + 1">Brak danych do wyświetlenia.</td>
      </tr>
      <tr v-for="(data, key1) in tableData" :key="data.id" class="m-datatable__row" v-else>
        <td>{{ serialNumber(key1) }}</td>
        <td v-for="(value, key) in data">{{ value }}</td>
      </tr>
      </tbody>
    </table>


  </div>
</template>

<script type="text/ecmascript-6">
import axios from 'axios';
import Vue from 'vue';
import 'vuejs-datatable/dist/themes/bootstrap-4.esm';
import {
  VuejsDatatableFactory,
  IDataFnParams,
  IDisplayHandlerParam,
  ITableContentParam,
  TColumnsDefinition,
  VueDatatable
} from 'vuejs-datatable';

Vue.use(VuejsDatatableFactory, VueDatatable);


export default {
  props: {
    fetchUrl: {type: String, required: true},
    columns: {type: Array, required: true},
  },
  data() {
    return {
      tableData: [],
      url: '',
      pagination: {
        meta: {to: 1, from: 1}
      },
      offset: 4,
      currentPage: 1,
      perPage: 1,
      sortedColumn: this.columns[0],
      order: 'asc',
      search: null,
      pageOptions: [1, 10, 20, 50],
    }
  },
  watch: {
    fetchUrl: {
      handler: function (fetchUrl) {
        this.url = fetchUrl
      },
      immediate: true
    }
  },
  created() {
    return this.fetchData()
  },
  computed: {
    /**
     * Get the pages number array for displaying in the pagination.
     * */
    pagesNumber() {
      if (!this.pagination.meta.to) {
        return []
      }
      let from = this.pagination.meta.current_page - this.offset
      if (from < 1) {
        from = 1
      }
      let to = from + (this.offset * 2)
      if (to >= this.pagination.meta.last_page) {
        to = this.pagination.meta.last_page
      }
      let pagesArray = []
      for (let page = from; page <= to; page++) {
        pagesArray.push(page)
      }
      return pagesArray
    },
    /**
     * Get the total data displayed in the current page.
     * */
    totalData() {
      return (this.pagination.meta.to - this.pagination.meta.from) + 1
    }
  },
  methods: {
    fetchData() {
      let dataFetchUrl = `${this.url}&page=${this.currentPage}&column=${this.sortedColumn}&order=${this.order}&per_page=${this.perPage}`
      axios.get(dataFetchUrl)
        .then(({data}) => {
          this.pagination = data
          this.tableData = data.data
        }).catch(error => this.tableData = [])
    },
    /**
     * Get the serial number.
     * @param key
     * */
    serialNumber(key) {
      return (this.currentPage - 1) * this.perPage + 1 + key
    },
    /**
     * Change the page.
     * @param pageNumber
     */
    changePage(pageNumber) {
      this.currentPage = pageNumber
      this.fetchData()
    },
    /**
     * Sort the data by column.
     * */
    sortByColumn(column) {
      if (column === this.sortedColumn) {
        this.order = (this.order === 'asc') ? 'desc' : 'asc'
      } else {
        this.sortedColumn = column
        this.order = 'asc'
      }
      this.fetchData()
    },
    handleSearch() {
      this.fetchData()
    },
    handlePerPage($event) {
      this.perPage = $event.target.value;
      this.fetchData()
    }
  },
  filters: {
    columnHead(value) {
      return value.split('_').join(' ').toUpperCase()
    }
  },
  translate: {
    nextButton: 'Dalej',
    previousButton: 'Wstecz',
    placeholderSearch: 'Szukaj...',
  },
  name: 'DataTable'
}
</script>

<style scoped>
</style>

Notes.vue:

<template>
  <CRow>
    <CCol col="12">
      <transition name="slide">
        <CCard>
          <CCardBody>
            <h4>
              Menus
            </h4>
            <CButton color="success" @click="createNote()" class="mb-3 my-5">Add Menu <font-awesome-icon icon="fas fa-plus" /> </CButton>
            <div class="flex-center position-ref full-height">
              <data-table
                :fetch-url="datatTableUrl"
                :columns="['name', 'email', 'id' , 'created_at']"
                :headers="['nazwa', 'adres email', 'ID' , 'utworzono']"
              ></data-table>
            </div>
          </CCardBody>
        </CCard>
      </transition>
    </CCol>
  </CRow>
</template>

<script>
import Vue from 'vue';

export default {
  data() {
    return {
      datatTableUrl: '',
    }
  },
  created: function () {
    this.datatTableUrl = Vue.prototype.$apiAdress + '/api/users/dataTable' + '?token=' + localStorage.getItem("api_token");
  },
  methods: {
    noteLink(id) {
      return `notes/${id.toString()}`
    },
    editLink(id) {
      return `notes/${id.toString()}/edit`
    },
    showNote(id) {
      const noteLink = this.noteLink(id);
      this.$router.push({path: noteLink});
    },
    editNote(id) {
      const editLink = this.editLink(id);
      this.$router.push({path: editLink});
    },
    deleteNote(id) {
      let self = this;
      let noteId = id;
      axios.post(this.$apiAdress + '/api/notes/' + id + '?token=' + localStorage.getItem("api_token"), {
        _method: 'DELETE'
      })
        .then(function (response) {
          self.message = 'Successfully deleted note.';
          self.showAlert();
          self.getNotes();
        }).catch(function (error) {
        console.log(error);
        self.$router.push({path: '/login'});
      });
    },
    createNote() {
      this.$router.push({path: 'notes/create'});
    },
  }
}
</script>

This code work fine.

I have 2 problems:

  1. I would like the column headers in the table to be taken from: headers – currently these are the column names from the database (ie: columns).

  2. I would like to add a link to edit and delete a record. I have created methods: editLink (), deleteNote (). How can I add them to my table? I would like them to be visible next to the "name" column

How can I make it?

Please help me 🙂

Answers:

For the problem #1. I would do it this way.
Merge the columns and the headers as one Object, ex: where the key will be the column name (Important: don’t forget to register the headers prop).

<data-table
     :fetch-url="datatTableUrl"
      :headers="{'name': 'nazwa','email': 'adres email','id': 'ID' , 'created_at': 'utworzono'}"
></data-table>

In the DataTable:

<th v-for="(column, label) in headers" :key="column" @click="sortByColumn(column)" class="table-head">
          {{ label | columnHead }}
          <span v-if="column === sortedColumn">
                            <font-awesome-icon v-if="order === 'asc' "  icon="fas fa-angle-up" />
                            <font-awesome-icon v-else icon="fas fa-angle-down" />
            </span>
        </th>

For the Second problem #2 (Not very clear), Better to move these functions to the DataTable and add the actions as a new column, short example:

<td><button @click="editLink(key1)">Edit link or some fa fa icon</button></td>

Add as prop in the DataTable:

props: {
    fetchUrl: {type: String, required: true},
    columns: {type: Array, required: true},
    headers: {type: Object, required: true} //<-- This
  },