A simple, clean data table for VueJS with essential features like sorting, column filtering, pagination etc
Did vue-good-table just save you a bunch of time? Use some of them extra minutes to spread the joy!
Hey there! coming from 1.x? find the upgrade guide here
Some example recipes for inspiration vue-good-table Recipes
- Getting Started
- Configuration
- Theme
- Advanced Customization
- Authors
- License
Install with npm:
npm install --save vue-good-table
Import into project:
import Vue from 'vue';
import VueGoodTable from 'vue-good-table';
// import the styles
import 'vue-good-table/dist/vue-good-table.css'
Vue.use(VueGoodTable);
<template>
<div>
<vue-good-table
:columns="columns"
:rows="rows"
:search-options="{
enabled: true,
}"
:pagination-options="{
enabled: true,
perPage: 5,
}"
styleClass="vgt-table striped bordered"/>
</div>
</template>
<script>
export default {
name: 'my-component',
data(){
return {
columns: [
{
label: 'Name',
field: 'name',
filterOptions: {
enabled: true,
},
},
{
label: 'Age',
field: 'age',
type: 'number',
},
{
label: 'Created On',
field: 'createdAt',
type: 'date',
dateInputFormat: 'YYYY-MM-DD',
dateOutputFormat: 'MMM Do YY',
},
{
label: 'Percent',
field: 'score',
type: 'percentage',
},
],
rows: [
{ id:1, name:"John", age: 20, createdAt: '201-10-31:9: 35 am',score: 0.03343 },
{ id:2, name:"Jane", age: 24, createdAt: '2011-10-31', score: 0.03343 },
{ id:3, name:"Susan", age: 16, createdAt: '2011-10-30', score: 0.03343 },
{ id:4, name:"Chris", age: 55, createdAt: '2011-10-11', score: 0.03343 },
{ id:5, name:"Dan", age: 40, createdAt: '2011-10-21', score: 0.03343 },
{ id:6, name:"John", age: 20, createdAt: '2011-10-31', score: 0.03343 },
{ id:7, name:"Jane", age: 24, createdAt: '20111031' },
{ id:8, name:"Susan", age: 16, createdAt: '2013-10-31', score: 0.03343 },
],
};
},
};
</script>
This should result in the screenshot seen above
These options relate to the table as a whole
Array containing objects that describe table columns. The column object itself can contain many configurable properties.
[
{
label: 'Name',
field: 'name',
filterable: true,
}
//...
]
Array containing row objects. Each row object contains data that will be displayed in the table row.
[
{
id:1,
name:"John",
age:20
},
//...
]
for grouped rows, you need a nested format. Refer to Grouped Rows for an example.
Enable Right-To-Left layout for the table
<vue-good-table
:columns="columns"
:rows="rows"
:rtl="true">
</vue-good-table>
Show line number for each row
<vue-good-table
:columns="columns"
:rows="rows"
:lineNumbers="true">
</vue-good-table>
Set of options related to table sorting
<vue-good-table
:columns="columns"
:rows="rows"
:sort-options="{
enabled: true,
initialSortBy: {field: 'name', type: 'asc'}
}">
</vue-good-table>
Enable/disable sorting on table as a whole.
<vue-good-table
:columns="columns"
:rows="rows"
:sort-options="{
enabled: true,
}">
</vue-good-table>
Allows specifying a default sort for the table on wakeup
<vue-good-table
:columns="columns"
:rows="rows"
:sort-options="{
enabled: true,
initialSortBy: {field: 'name', type: 'asc'}
}">
</vue-good-table>
// in data
defaultSort: {
field: 'name',
type: 'asc' //asc or desc (default: 'asc')
}
A set of options that are related to table pagination. Each of these are optional and reasonable defaults will be used if you leave off the property.
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
perPage: 5,
position: 'top',
perPageDropdown: [3, 7, 9],
dropdownAllowAll: false,
setCurrentPage: 2,
nextLabel: 'next',
prevLabel: 'prev',
rowsPerPageLabel: 'Rows per page',
ofLabel: 'of',
allLabel: 'All',
}">
</vue-good-table>
Options explained below
Enable Pagination for table. By default the paginator is created at the bottom of the table.
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true
}">
</vue-good-table>
Add pagination on 'top'
, 'bottom'
, or 'both'
(top and bottom) of the table (default position is bottom)
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
position: 'both'
}">
</vue-good-table>
Number of rows to show per page
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
perPage: 5
}">
</vue-good-table>
Customize the dropdown options for the amount of items per page
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
perPageDropdown: [3, 7, 9]
}">
</vue-good-table>
enables/disables 'All' in the per page dropdown.
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
perPageDropdown: [3, 7, 9],
dropdownAllowAll: false,
}">
</vue-good-table>
set current page programmatically.
There's no validation for number of pages so please be careful using this.
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
setCurrentPage: 2,
}">
</vue-good-table>
you can change one or more of the texts shown on pagination by overriding the labels in the following way:
<vue-good-table
:columns="columns"
:rows="rows"
:paginationOptions="{
enabled: true,
nextLabel: 'next',
prevLabel: 'prev',
rowsPerPageLabel: 'Rows per page',
ofLabel: 'of',
allLabel: 'All',
}">
</vue-good-table>
Set of search related options. These options pertain to the global table search.
<vue-good-table
:columns="columns"
:rows="rows"
:searchOptions="{
enabled: true,
trigger: 'enter',
searchFn: mySearchFn,
placeholder: 'Search this table',
externalQuery: searchQuery
}">
</vue-good-table>
Search options explained below
Allows a single search input for the whole table
Note: enabling this option disables column filters
<vue-good-table
:columns="columns"
:rows="rows"
:searchOptions="{
enabled: true
}">
</vue-good-table>
Allows you to specify if you want search to trigger on 'enter' event of the input. By default table searches on key-up.
<vue-good-table
:columns="columns"
:rows="rows"
:searchOptions="{
enabled: true,
trigger: 'enter'
}">
</vue-good-table>
Allows you to specify your own search function for the global search
<vue-good-table
:columns="columns"
:rows="rows"
:searchOptions="{
enabled: true,
searchFn: myFunc
}">
</vue-good-table>
// in js
methods: {
myFunc(row, col, cellValue, searchTerm){
return cellValue === 'my value';
},
}
Text for global search input place holder
<vue-good-table
:columns="columns"
:rows="rows"
:searchOptions="{
enabled: true,
placeholder: 'Search this table',
}">
</vue-good-table>
If you want to use your own input for searching the table, you can use this property
<input type="text" v-model="searchTerm" >
<vue-good-table
:columns="columns"
:rows="rows"
:searchOptions="{
enabled: true,
externalQuery: searchTerm
}">
</vue-good-table>
// and in data
data(){
return {
searchTerm: '',
// rows, columns etc...
};
}
Creating table with selectable rows (checkboxes) is easier than ever.
Object containing select options
<vue-good-table
@on-select-all="allSelected"
@on-row-click="rowSelected"
:columns="columns"
:rows="rows"
:select-options="{
enabled: true,
selectionInfoClass: 'custom-class',
selectionText: 'rows selected',
clearSelectionText: 'clear',
}">
you can also programmatically get selected rows at any time by putting a ref
on the table and then doing
this.$refs['my-table'].selectedRows;
Check out a working example for details
Sometimes you have a hierarchy in table and you want to group rows under subheadings, vue-good-table allows you to do that as well. Following properties relate to row grouping
Object containing group related options.
<vue-good-table
:columns="columns"
:rows="rows"
:group-options="{
enabled: true,
headerPosition: 'bottom'
}">
rows are formatted differently for grouped tables, refer to Grouped Rows section.
Style options for table
Allows applying your own classes to table. Other in-built classes: condensed, striped, bordered
<vue-good-table
:columns="columns"
:rows="rows"
styleClass="vgt-table bordered striped">
</vue-good-table>
Allows providing custom styles for rows. It can be a string: 'my-class' or a function.
<vue-good-table
:columns="columns"
:rows="rows"
:rowStyleClass="myStyleFn">
</vue-good-table>
// in methods
myStyleFn(row){
// if row has something return a specific class
if(row.fancy) {
return 'fancy-class';
}
return '';
}
Allows using other themes. Currently there is one other theme - 'nocturnal'
<vue-good-table
:columns="columns"
:rows="rows"
theme="nocturnal">
</vue-good-table>
Each column objects can contain the following configuration options:
Text to put on column header.
columns: [
{
label: 'name'
},
// ...
]
Row object property that this column corresponds to. This can be:
- String
eg: 'name'
- simple row property name - String
eg: 'location.lat'
- nested row property name. lets say if the row had a property 'location' which was an object containing 'lat' and 'lon' - Function - a function that returns a value to be displayed based on the row object
columns: [
{
label: 'name',
field: this.fealdFn,
},
// ...
]
// in methods
fieldFn(rowObj) {
return rowObj.name;
}
type of column. default: 'text'. This determines the formatting for the column and filter behavior as well. Possible values:
- number - right aligned
- decimal - right aligned, 2 decimal places
- percentage - expects a decimal like 0.03 and formats it as 3.00%
- date - expects a string representation of date eg
'20170530'
. You should also specify dateInputFormat and dateOutputFormat
columns: [
{
label: 'joined On',
field: 'createdAt',
type: 'date',
dateInputFormat: 'YYYY-MM-DD', // expects 2018-03-16
dateOutputFormat: 'MMM Do YYYY', // outputs Mar 16th 2018
},
// ...
]
provide the format to parse date string
provide the format for output date
enable/disable sorting on columns. This property is higher priority than global sortable property
columns: [
{
label: 'name',
field: 'user_name',
sortable: false,
},
// ...
]
custom sort function. If you want to supply your own sort function you can use this property.
// in data
column: [
{
label: 'Name',
field: 'name',
sortable: true,
sortFn: this.sortFn,
}
//...
],
// in methods
methods: {
sortFn(x, y, col, rowX, rowY) {
// x - row1 value for column
// y - row2 value for column
// col - column being sorted
// rowX - row object for row1
// rowY - row object for row2
return (x < y ? -1 : (x > y ? 1 : 0));
}
}
Allows for custom format of values, function(value)
, should return the formatted value to display.
// in data
column: [
{
label: 'Salary',
field: 'salary',
sortable: true,
formatFn: this.formatFn,
}
//...
],
// in methods
formatFn: function(value) {
return '$' + value;
}
indicates whether this column will require html rendering.
The preferred way of creating columns that have html is by using slots
// in data
column: [
{
label: 'Action',
field: 'btn',
html: true,
}
//...
],
rows: [
{
btn: '<button>My Action</button>',
// ...
}
]
provide a width value for this column
columns: [
{
label: 'name',
field: 'user_name',
width: '50px',
},
// ...
]
hidden Boolean
hide a column
columns: [
{
label: 'name',
field: 'user_name',
hidden: true,
},
// ...
]
provide custom class(es) to the table header
columns: [
{
label: 'name',
field: 'user_name',
thClass: 'custom-th-class',
},
// ...
]
provide custom class(es) to the table cells
columns: [
{
label: 'name',
field: 'user_name',
tdClass: 'text-center',
},
// ...
]
if true, this column will be ignored by the global search
columns: [
{
label: 'name',
field: 'user_name',
globalSearchDisabled: true,
},
// ...
]
A collection of filter specific properties. You can find more about these properties in column filter options section
columns: [
{
label: 'name',
field: 'user_name',
filterOptions: {
enabled: true, // enable filter for this column
placeholder: 'Filter This Thing', // placeholder for filter input
filterValue: 'Jane', // initial populated value for this filter
filterDropdownItems: [], // dropdown (with selected values) instead of text input
filterFn: this.columnFilterFn, //custom filter function that
},
},
// ...
]
Some filterOption properties need a little more explanation
allows creating a dropdown for filter as opposed to an input
//array
filterDropdownItems: ['Blue', 'Red', 'Yellow']
//or
filterDropdownItems: [
{ value: 'n', text: 'Inactive' },
{ value: 'y', text: 'Active' },
{ value: 'c', text: 'Check' }
],
Custom filter, function of two variables: function(data, filterString)
, should return true if data matches the filterString, otherwise false
filterFn: function(data, filterString) {
var x = parseInt(filterString)
return data >= x - 5 && data <= x + 5;
}
// would create a filter matching numbers within 5 of the provided value
event emitted on table row click
<vue-good-table
:columns="columns"
:rows="rows"
@on-row-click="onRowClick">
methods: {
onRowClick(row, pageIndex, selected) {
// row - row object
// pageIndex - index of this row on the current page.
// selected - if selection is enabled this argument
// indicates selected or not
}
}
event emitted on table cell click
<vue-good-table
:columns="columns"
:rows="rows"
@on-cell-click="onCellClick">
methods: {
onCellClick(row, column, rowIndex) {
// row - row object
// column - column object
// rowIndex - index of this row on the current page.
}
}
event emitted on row mouseenter
<vue-good-table
:columns="columns"
:rows="rows"
@on-row-mouseenter="onRowMouseover">
methods: {
onRowMouseover(row, pageIndex) {
// row - row object
// pageIndex - index of this row on the current page.
}
}
event emitted on table row mouseleave
<vue-good-table
:columns="columns"
:rows="rows"
@on-row-mouseleave="onRowMouseleave">
methods: {
onRowMouseleave(row, pageIndex) {
// row - row object
// pageIndex - index of this row on the current page.
}
}
event emitted on global search (when global search is enabled)
<vue-good-table
:columns="columns"
:rows="rows"
@on-search="onSearch">
methods: {
onSearch(searchTerm, rowCount) {
// searchTerm - term being searched for
// rowCount - number of rows that match search
}
}
event emitted on pagination page change (when pagination is enabled)
<vue-good-table
:columns="columns"
:rows="rows"
@on-page-change="onPageChange">
methods: {
onPageChange(currentPage, currentPerPage, total) {
// currentPage - current page that pagination is at
// currentPerPage - number of items per page
// total - total number of items in the table
}
}
event emitted on per page dropdown change (when pagination is enabled)
<vue-good-table
:columns="columns"
:rows="rows"
@on-per-page-change="onPageChange">
methods: {
onPageChange(currentPage, currentPerPage, total) {
// currentPage - current page that pagination is at
// currentPerPage - number of items per page
// total - total number of items in the table
}
}
event emitted on sort change
<vue-good-table
:columns="columns"
:rows="rows"
@on-sort-change="onSortChange">
methods: {
onSortChange(sortType, columnIndex) {
// sortType - ascending or descending
// columnIndex - index of column being sorted
}
}
event emitted when all is selected (only emitted for checkbox tables)
<vue-good-table
:columns="columns"
:rows="rows"
@on-select-all="onSelectAll">
methods: {
onSelectAll(selected, selectedRows) {
// selected - whether the select-all checkbox is checked or unchecked
// selectedRows - all rows that are selected (this page)
}
}
Vue-good-table allows providing your own css classes for the table via styleClass option but it also has in-built classes that you can make use of
Vue-good-table currently comes in two themes
vue-good-table also supports dynamic td templates where you dictate how to display the cells. Example:
<vue-good-table
:columns="columns"
:rows="rows">
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'age'">
age: {{props.row.age}}
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
Note:
- The original row object can be accessed via
props.row
- The currently displayed table row index can be accessed via
props.index
. - The original row index can be accessed via
props.row.originalIndex
. You can then access the original row object by usingrows[props.row.originalIndex]
. - The column object can be accessed via
props.column
- You can access the formatted row data (for example - formatted date) via
props.formattedRow
Sometimes you might want to use custom column formatting. You can do that in the following way
<vue-good-table
:columns="columns"
:rows="rows">
<template slot="table-column" slot-scope="props">
<span v-if="props.column.label =='Name'">
<i class="fa fa-address-book"></i> {{props.column.label}}
</span>
<span v-else>
{{props.column.label}}
</span>
</template>
</vue-good-table>
To create grouped rows, you need two things.
- add groupOptions to table component
<vue-good-table
:columns="columns"
:rows="rows"
:groupOptions="{
enabled: true
}">
</vue-good-table>
- make sure the rows are formatted correctly. grouped rows need to be nested with headers rows containing rows in their children property. For example:
rows: [{
mode: 'span', // span means this header will span all columns
label: 'Header Two', // this is the label that'll be used for the header
children: [
{ name: 'Chris', age: 55, createdAt: '2011-10-11', score: 0.03343 },
{ name: 'Dan', age: 40, createdAt: '2011-10-21', score: 0.03343 },
]
}]
- sometimes, you might want a summary row instead of a header row. for example if you want to show total score for your group
rows: [{
name: 'Total', // this is the label that'll be used for the header
age: undefined,
createdAt: undefined,
score: 0.3, // total score here
children: [
{ name: 'Chris', age: 55, createdAt: '2011-10-11', score: 0.03343 },
{ name: 'Dan', age: 40, createdAt: '2011-10-21', score: 0.03343 },
]
}]
- if you want the header/summary row to show up at the bottom of the group, you can specify that in the groupOptions property of the table.
<vue-good-table
:columns="columns"
:rows="rows"
:groupOptions="{
enabled: true,
headerPosition: 'bottom',
}">
</vue-good-table>
you can check out some live examples on the recipes page: vue-good-table Recipes
If you want to add table specific actions like a print button for example, you can use the Table Actions Slot. If you have global search enabled, the action panel will show up to the right of that.
<vue-good-table
:columns="columns"
:rows="rows">
<div slot="table-actions">
This will show up on the top right of the table.
</div>
</vue-good-table>
You can provide html for empty state slot as well. Example:
<vue-good-table
:columns="columns"
:rows="rows">
<div slot="emptystate">
This will show up when there are no columns
</div>
</vue-good-table>
This project is licensed under the MIT License - see the LICENSE.md file for details