Last active
November 24, 2021 12:19
-
-
Save AbdullahiAbdulkabir/72442f86d7359985f5184c88833bd4d6 to your computer and use it in GitHub Desktop.
Data table component for vue expecting data been paginated informations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="dataTables_wrapper dt-bootstrap4 no-footer"> | |
<spinner | |
:showFull="true" | |
:loadingText="actionTypeText" | |
v-if="processingStuff || showFullLoader" | |
></spinner> | |
<div class="row"> | |
<div class="col-sm-12 col md-6"> | |
<div class="btn-group" role="group" aria-label="Actions"> | |
<button | |
type="button" | |
class="btn btn-primary" | |
@click="exportToCSV" | |
:disabled="filteredList.length === 0" | |
v-bind:class="{disabled: filteredList.length === 0}" | |
> | |
<i class="mdi mdi-file-export"></i> Export | |
</button> | |
</div> | |
</div> | |
<div class="col-sm-12 col-md-6"></div> | |
</div> | |
<div class="gap"></div> | |
<div class="row"> | |
<div class="col-sm-12 col-md-6"> | |
<div class="dataTables_length" id="order-listing_length"> | |
<label> | |
Show | |
<select | |
name="order-listing_length" | |
aria-controls="order-listing" | |
class="custom-select custom-select-sm form-control" | |
:disabled="filteredList.length === 0" | |
v-model="entriesPerPage" | |
> | |
<option value="6">6</option> | |
<option value="12">12</option> | |
<option value="18">18</option> | |
<option value="24">24</option> | |
<option :value="totalData">All</option> | |
</select> entries | |
</label> | |
</div> | |
</div> | |
<div class="col-sm-12 col-md-6"> | |
<div class="dataTables_filter"> | |
<label> | |
<input | |
type="text" | |
class="form-control" | |
v-model="search" | |
placeholder="Search" | |
ref="tableOne" | |
:disabled="loading && filteredList.length === 0" | |
/> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-sm-12 table-responsive"> | |
<table class="table dataTable no-footer" role="grid" style=" width:100%"> | |
<thead> | |
<tr> | |
<th scope="col" v-for="(column, columnIndex) in columns" :key="columnIndex"> | |
{{column.label}} | |
<i | |
class="mdi" | |
:title="'Sort by '+ column.label+ ' in ascending order'" | |
@click="sort(column.field, 'asc')" | |
:class="[currentSort === column.field && currentSortDir === 'asc' ? 'mdi-arrow-up-bold disabled' : 'mdi-arrow-up mdi-pointer']" | |
></i> | |
<i | |
class="mdi" | |
:title="'Sort by '+ column.label+ ' in descending order'" | |
@click="sort(column.field, 'desc')" | |
:class="[currentSort === column.field && currentSortDir === 'desc' ? 'mdi-arrow-down-bold disabled' : 'mdi-arrow-down mdi-pointer']" | |
></i> | |
</th> | |
<th v-if="actions.length > 0"></th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr v-for="(item, index) in (sortedActivity, filteredList)" v-bind:key="index"> | |
<td | |
v-for="(col, colIndex) in columns" | |
:key="colIndex" | |
v-html="itemValue(item, col.field)" | |
>{{itemValue(item, col.field)}}</td> | |
<!--v-if="dontShowKey === action && dontShowValue === action.dontShowKey"--> | |
<td v-if="actions.length > 0"> | |
<span v-for="(action, actionIndex) in actions" :key="actionIndex"> | |
<button | |
v-if="(typeof action.showKey === 'undefined' || action.showKey.length === 0 || action.showWhen.indexOf(item[action.showKey]) !== -1) | |
&& !action.hasOwnProperty('dropdown')" | |
:title="action.title || ''" | |
:class="action.class" | |
v-bind="$attrs" | |
@click="handleCallback(action.callback, filterObject(item, action.args))" | |
>{{ action.text }}</button> | |
<div | |
class="btn-group" | |
v-else-if="(typeof action.showKey === 'undefined' || action.showKey.length === 0 || action.showWhen.indexOf(item[action.showKey]) !== -1) | |
&& action.hasOwnProperty('dropdown')" | |
> | |
<button | |
:class="action.class" | |
class="dropdown-toggle" | |
:aria-expanded="true" | |
data-toggle="dropdown" | |
:title="action.title || ''" | |
>{{ action.title }}</button> | |
<div class="dropdown-menu"> | |
<span | |
v-for="(dropDown, dropDownIndex) in action.dropdown" | |
:key="dropDownIndex" | |
> | |
<a | |
class="dropdown-item" | |
v-bind="$attrs" | |
v-if="typeof dropDown.showKey === 'undefined' || dropDown.showKey.length === 0 || dropDown.showWhen.indexOf(item[dropDown.showKey]) !== -1" | |
@click="handleCallback(dropDown.callback, filterObject(item, dropDown.args))" | |
:title="dropDown.title || ''" | |
>{{dropDown.text}}</a> | |
</span> | |
</div> | |
</div> | |
</span> | |
</td> | |
</tr> | |
<tr v-if="filteredList.length === 0"> | |
<td | |
v-if="search.length > 0" | |
colspan="100" | |
style="text-align: center" | |
>Nothing was found for {{search}}</td> | |
</tr> | |
<tr v-if="loading"> | |
<td style="text-align: center" colspan="100"> | |
<spinner></spinner> | |
</td> | |
</tr> | |
<tr v-else-if="!loading && data.length === 0"> | |
<td style="text-align: center" colspan="100"> | |
<span>No data yet!</span> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<div class="row" v-if="filteredList.length > 0"> | |
<div class="col-sm-12 col-md-5" v-if="data.length > 0"> | |
<div | |
class="dataTables_info" | |
>Showing page {{ currentPage }} of {{availableTotalPage}} page(s)</div> | |
</div> | |
<div class="col-sm-12 col-md-7"> | |
<div class="dataTables_paginate paging_simple_numbers"> | |
<ul class="pagination"> | |
<li | |
class="paginate_button page-item previous" | |
v-bind:class="{disabled: (currentPage === 1)}" | |
> | |
<a tabindex="0" class="page-link" @click="prevPage">Previous</a> | |
</li> | |
<li | |
class="paginate_button page-item next" | |
v-bind:class="{disabled: (availableTotalPage === 1)}" | |
> | |
<a tabindex="0" class="page-link" @click="nextPage">Next</a> | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
import Spinner from "./../Loading"; | |
import { ExportToCsv } from "export-to-csv"; | |
export default { | |
name: "TabulatedData", | |
components: { Spinner }, | |
mounted: function() { | |
this.entriesPerPage = this.perPageEntry; | |
this.showPerPage = this.entriesPerPage; | |
}, | |
watch: { | |
entriesPerPage: function(val) { | |
this.entriesPerPage = val === 0 ? this.data.length : val; | |
this.showPerPage = this.entriesPerPage; | |
}, | |
filteredList: function(val) { | |
this.filteredList = val; | |
} | |
}, | |
props: { | |
sortBy: { | |
type: String, | |
default: "default" | |
}, | |
perPageEntry: { | |
type: Number, | |
default: 12 | |
}, | |
sortDir: { | |
type: String, | |
default: "default" | |
}, | |
columns: { | |
type: Array, | |
default: () => { | |
return []; | |
} | |
}, | |
data: { | |
type: Array, | |
default: () => { | |
return []; | |
} | |
}, | |
actions: { | |
type: Array, | |
default: () => { | |
return []; | |
} | |
}, | |
loading: { | |
type: Boolean, | |
default: true | |
}, | |
showFullLoader: { | |
type: Boolean, | |
default: false | |
}, | |
type: { | |
type: String, // striped | hover | |
default: "striped" | |
}, | |
darkMode: { | |
type: Boolean, | |
default: false | |
}, | |
csvFileName: { | |
type: String, | |
default: "CSV Exported at " + new Date().toLocaleTimeString() | |
} | |
}, | |
data: () => ({ | |
actionTypeText: "loading", | |
processingStuff: false, | |
currentSort: "default", | |
currentSortDir: "default", | |
showPerPage: 0, | |
availableTotalPage: 0, | |
search: "", | |
searchSelection: "", | |
currentPage: 1, | |
disable: "", | |
entriesPerPage: 0, | |
disableNext: "", | |
allPages: [ | |
{ label: 6, value: 6 }, | |
{ label: 12, value: 12 }, | |
{ label: 18, value: 18 }, | |
{ label: 24, value: 24 }, | |
{ label: "All", value: 0 } | |
] | |
}), | |
methods: { | |
filterObject(haystack, needle = []) { | |
let data = []; | |
if (needle.length === 0 || haystack.length == 0) return []; | |
needle.forEach(key => { | |
if (haystack.hasOwnProperty(key)) data.push(haystack[key]); | |
}); | |
return data; | |
}, | |
handleCallback(callback, argument) { | |
if (!callback) return; | |
this.$emit(callback, ...argument); | |
}, | |
sort: function(s, sortByDir = false) { | |
if (s === this.currentSort && !sortByDir) { | |
this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc"; | |
} else if (s === this.currentSort && sortByDir) | |
this.currentSortDir = sortByDir; | |
this.currentSort = s; | |
}, | |
nextPage: function() { | |
if (this.currentPage * this.showPerPage < this.data.length) | |
this.currentPage++; | |
window.scroll(0, 0); | |
}, | |
prevPage: function() { | |
if (this.currentPage > 1) this.currentPage--; | |
window.scroll(0, 0); | |
}, | |
hasValue(item, column) { | |
return item[column.toLowerCase()] !== "undefined"; | |
}, | |
itemValue(item, column) { | |
return item[column]; | |
}, | |
startProcessingStuff: function(text) { | |
this.actionTypeText = text; | |
this.processingStuff = true; | |
}, | |
doneProcessingStuff() { | |
this.processingStuff = false; | |
}, | |
exportToCSV: function() { | |
this.startProcessingStuff("Exporting to CSV"); | |
let headers = []; | |
let columns = this.columns.filter(column => { | |
return ( | |
!column.hasOwnProperty("skipExport") && column.skipExport !== true | |
); | |
}); | |
columns.map(column => headers.push(column.label)); | |
const options = { | |
fieldSeparator: ",", | |
quoteStrings: '"', | |
decimalSeparator: ".", | |
showLabels: true, | |
showTitle: false, | |
title: this.csvFileName, | |
filename: this.csvFileName, | |
useTextFile: false, | |
useBom: true, | |
useKeysAsHeaders: false, | |
headers: headers | |
// headers: ['Column 1', 'Column 2', etc...] <-- Won't work with useKeysAsHeaders present! | |
}; | |
const csvExporter = new ExportToCsv(options); | |
let data = []; | |
this.data.forEach(datum => { | |
let currentData = {}; | |
// Loop through only the columns that needs to be exported | |
columns.forEach(column => { | |
// Add Data for only the column to be exported | |
currentData[[column.field]] = datum[column.field]; | |
}); | |
data.push(currentData); | |
}); | |
csvExporter.generateCsv(data); | |
this.doneProcessingStuff(); | |
this.$toastr.clear(); | |
this.$toastr.success( | |
this.csvFileName + " was exported successfully!", | |
"Export successfully", | |
{ timeOut: 5000 } | |
); | |
} | |
}, | |
computed: { | |
sortedActivity: function() { | |
return this.data | |
.sort((a, b) => { | |
// Set the sorting to the first column of header | |
// Order by desc | |
this.currentSort = | |
this.columns.length > 0 && this.currentSort === "default" | |
? this.columns[0] | |
: this.currentSort; | |
let modifier = 1; | |
if (this.currentSortDir === "desc") modifier = -1; | |
if (a[this.currentSort] < b[this.currentSort]) return -1 * modifier; | |
if (a[this.currentSort] > b[this.currentSort]) return 1 * modifier; | |
return 0; | |
}) | |
.filter((row, index) => { | |
let start = (this.currentPage - 1) * this.entriesPerPage; | |
let end = this.currentPage * this.entriesPerPage; | |
if (index >= start && index < end) return true; | |
}); | |
}, | |
filteredList: { | |
get: function() { | |
return this.data | |
.filter(val => { | |
if (this.search.length === 0) return true; | |
let match = false; | |
let keys = Object.keys(val); | |
for (let i = 0; i < keys.length; i++) { | |
let current = val[keys[i]] || ""; | |
match = current | |
.toString() | |
.toUpperCase() | |
.match(this.search.toString().toUpperCase()); | |
if (match) break; | |
} | |
return match; | |
}) | |
.filter((row, index) => { | |
let start = (this.currentPage - 1) * this.entriesPerPage; | |
let end = this.currentPage * this.entriesPerPage; | |
if (index >= start && index < end) return true; | |
}); | |
}, | |
set: function(val) { | |
this.totalPages = val.length; | |
} | |
}, | |
totalPages: { | |
get: function() { | |
return Math.ceil(this.data.length / this.entriesPerPage); | |
}, | |
set: function() { | |
let dataLength = | |
this.search.length > 0 ? this.filteredList.length : this.data.length; | |
this.availableTotalPage = Math.ceil(dataLength / this.entriesPerPage); | |
} | |
}, | |
totalData() { | |
return this.data.length; | |
} | |
}, | |
created() { | |
// Init the Sort By | |
this.currentSort = this.sortBy; | |
this.currentSortDir = this.sortDir; | |
if (this.currentPage === 1) this.disable = "disabled"; | |
if (this.currentPage * this.entriesPerPage < this.data.length) | |
this.disableNext = "disabled"; | |
} | |
}; | |
</script> | |
<style> | |
.gap { | |
margin-bottom: 20px; | |
} | |
.mdi-pointer { | |
cursor: pointer; | |
} | |
a:not([href]):not([tabindex]) { | |
cursor: pointer; | |
} | |
.table th img, | |
.jsgrid .jsgrid-table th img, | |
.table td img, | |
.jsgrid .jsgrid-table td img { | |
width: 80px !important; | |
height: 80px !important; | |
border-radius: 100% !important; | |
} | |
.dataTables_wrapper .dataTable .btn, | |
.dataTables_wrapper .dataTable .fc button, | |
.fc .dataTables_wrapper .dataTable button, | |
.dataTables_wrapper .dataTable .ajax-upload-dragdrop .ajax-file-upload, | |
.ajax-upload-dragdrop .dataTables_wrapper .dataTable .ajax-file-upload, | |
.dataTables_wrapper .dataTable .swal2-modal .swal2-buttonswrapper .swal2-styled, | |
.swal2-modal .swal2-buttonswrapper .dataTables_wrapper .dataTable .swal2-styled, | |
.dataTables_wrapper .dataTable .wizard > .actions a, | |
.wizard > .actions .dataTables_wrapper .dataTable a { | |
padding: 0.5rem 1rem; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment