Components to render a table using client or remote data
npm install @myena/vue-table
- Vue 2
- Bootstrap 3
- FontAwesome 4
https://myena-vue-table.netlify.app/
Vue component for rendering a client side table with pagination, grouping, sorting, filtering, details row. Entire table data should be given to the table and will be paginated client side. Data can come from a Vuex store.
<EnaTableClient :columns="columns" :options="options" :data="data" :loading="loading">
<my-custom-filter-component slot="filter" v-on:search="search"></my-custom-filter-component>
<span slot="__group_meta" slot-scope="{ data }">
<strong><span v-html="data.label"></span> (Total in group: {{data.total}})</strong>
</span>
<template slot="heading_column1">
<div>Custom heading of column1</div>
</template>
<template slot="column_column1" slot-scope="{ row }" >
<span><i v-if="row.column2 === 'disable'" class="fa fa-ban"></i> {{row.column1}}</span>
</template>
<template slot="details_row" slot-scope="{ row }">
<div>Div for details (expanded) row</div>
</template>
</EnaTableClient>
// this will create the global (window) object "EnaTableClient"
import '@myena/vue-table/dist/EnaTableClient';
// or include as script in html : <script type="text/javascript" src="node_modules/@myena/vue-table/dist/EnaTableClient/index.js"></script>
// registers the component globally
// in the view that contains the table
const MyView = new Vue({
data() {
// Define properties
return {
/**
* List of objects to present in the table
*
* @member
* @type {Array}
*/
data: Array,
/**
* List of keys to use from each object (table columns)
*
* @type {Array}
*/
columns: Array,
/**
* The filter object. If updated will filter the results by the value
*
* @type {Object}
*/
filter: {
type: Object,
default: () => ({
// The search query string. If updated will filter the results by the value
keyword: '',
}),
},
/**
* Loading indicator. If true, will display the `loadingMsg` instead of the body
* @type {Boolean}
*/
loading: {
type: Boolean,
default: false,
},
/**
* Options for the table
*
* @inner
* @type {Object}
*/
options: {
/**
* Classes to use on various elements
*
* @inner
* @type {Object}
*/
classes: {
wrapper: 'table-responsive',
table: 'table',
formControl: 'form-control',
sort: {
none: 'fa fa-sort',
ascending: 'fa fa-sort-asc',
descending: 'fa fa-sort-desc',
},
pagination: {
wrapper: 'pagination',
info: 'info form-inline',
first: 'fa fa-angle-double-left',
prev: 'fa fa-angle-left',
next: 'fa fa-angle-right',
last: 'fa fa-angle-double-right',
},
group: {
show: 'fa fa-chevron-right',
hide: 'fa fa-chevron-down',
},
},
/**
* Key-value pairs with the headings to overwrite (label to display)
* can also be overwritten with slot: "heading_colname"
*
* @inner
* @type {Object}
*/
headings: {},
/**
* Key-value pairs with templates (components) for the column value
*
* @type {Object}
*/
templates: {},
/**
* Key-value pairs with custom search function per column,
* or false to disable search for that column
*
* @type {Object}
*/
search: {},
/**
* Field to group by - key name
*
* @default
* @type {Boolean|String}
*/
groupBy: false,
/**
* Expand/collapse groups
*
* @default
* @type {Boolean}
*/
toggleGroups: false,
/**
* Object of data to use for each group "header" (key is the group value)
*
* @type {Object}
*/
groupMeta: {},
/**
* Required, unique identifier
*
* @default
* @type {String}
*/
uniqueKey: 'id',
/**
* show extra row for each row with details
*
* @default
* @type {Boolean}
*/
detailsRow: false,
/**
* Texts
*
* @type Object
*/
text: {
/**
* Text to show when row can be expanded
* @type {String}
*/
expand: 'Show details',
/**
* Text to show when row can be collapsed
* @type {String}
*/
collapse: 'Hide details',
/**
* Message to show when there is no data
* @type {String}
*/
noData: 'No data to show',
/**
* Message to show when no results are found for the search
* @type {String}
*/
emptyResults: 'No results for this filter',
/**
* Message to show when no results are found for the search
* @type {String}
*/
loading: 'Loading ...',
/**
* Text to show for pagination helper buttons
* @type {Object}
*/
pagination: {
next: '',
last: '',
info: {
showing: 'Showing %s to %s of %s rows.',
records: 'records per page',
noRows: 'No rows to display',
},
},
},
/**
* empty object to disable sorting for all,
* or define what columns are sortable; defaults to all sortable
*
* @default
* @type {true|Object}
*/
sortable: true,
/**
* false, to disable pagination - show all; defaults to true
*
* @default
* @type {Boolean}
*/
pagination: true,
/**
* number of items per page
*
* @default
* @type {Number}
*/
perPage: 10,
/**
* How many pages to show in the paginator. Odd number
*
* @default
* @type {Number}
*/
pageInterval: 7,
/**
* values to show in the selector of items per page
*
* @default
* @type {Array}
*/
perPageValues: [1, 2, 5, 10, 20, 50],
/**
* Is the table editable (eg: can select value)
* @type {Boolean}
*/
editable: false,
/**
* List of columns that should be disabled for click to select/deselect
* @type {Array}
*/
nonSelectableColumns: [],
/**
* Object (key, order) to sort table by on first load (on created)
* @type {Object}
*/
sortBy: {
column: null,
order: null,
},
/**
* The collator used for sorting
* @type {Intl.Collator}
*/
sortCollator: new Intl.Collator('en', {
numeric: true,
sensitivity: 'base',
}),
},
};
},
// OR use computed properties instead
computed: {
data() {
return this.$store.state.myListData; // or any other source that has all data
},
},
methods: {
search(filter) {
this.searchQuery = filter.keyword;
},
},
watch: {
data(data) {
// set the group metas when data changes
// groupColumn is the same as the one used for 'groupBy' option
this.options.groupMeta = data.reduce((groupMeta, r) => {
if (!groupMeta[r.groupColumn]) {
groupMeta[r.groupColumn] = {
label: r.groupColumn,
total: 0,
};
}
groupMeta[r.groupColumn].total += 1;
return groupMeta;
}, {});
},
},
});
import { Client } from '@myena/vue-table';
// in the view that contains the table
const MyView = new Vue({
components: {
ClientTable: Client,
},
data() {
return {
columns: ['column1', 'column2'],
options: {
},
};
},
computed: {
data() {
return this.$store.state.myListData; // or any other source that has all data
},
},
});
Vue component for rendering a table that loads data from the server, with pagination, sorting, filtering, details row. It doesn't support grouping.
<EnaTableServer :columns="columns" :options="options" ref="serverTable">
<div slot="filter">
<input placeholder="Search by name" v-model="options.filter.name"/>
<button @click="filter">Find</button>
</div>
<template slot="heading_column1">
<div>Custom heading of column1</div>
</template>
<template slot="column_column1" slot-scope="{ row }" >
<span><i v-if="row.column2 === 'disable'" class="fa fa-ban"></i> {{row.column1}}</span>
</template>
<template slot="details_row" slot-scope="{ row }">
<div>Div for details (expanded) row</div>
</template>
</EnaTableServer>
import axios from 'axios';
import Qs from 'qs';
import { Server: ServerTable } from '@myena/vue-table';
const myServerTable = {
extends: ServerTable,
methods: {
/**
* Override the fetch method
*/
async fetch(params) {
const { data } = await axios.get(this.url, {
params: Object.assign({}, params, {
filter: this.options.filter,
}),
paramsSerializer(p) {
return Qs.stringify(p, { arrayFormat: 'brackets' });
},
});
return data;
},
/**
* Override the parse method to return `data` and `total` fields
*/
parse({ list, total }) {
return {
data: list,
total,
};
},
},
};
export default {
name: 'app',
components: {
ServerTable: myServerTable,
},
data: () => ({
columns: ['name', 'capital', 'population'],
url: 'https://us-central1-vue-myena-table.cloudfunctions.net/countries',
options: {
perPage: 5,
uniqueKey: 'alpha3Code',
// not handled by the component
// added here for easier access in the overridden fetch function
filter: {
name: null,
},
},
}),
methods: {
filter() {
// call component loadData, which sets some params then calls fetch (above)
this.$refs.serverTable.loadData();
},
},
};