This feature is not available on mobile when
mobile-cards
.
To show a data table is very simple, you just need a data
and a columns
prop.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<b-table :data="data" :columns="columns"></b-table>
</template>
<script>
export default {
data() {
return {
data: [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
],
columns: [
{
field: 'id',
label: 'ID',
width: '40',
numeric: true
},
{
field: 'first_name',
label: 'First Name',
},
{
field: 'last_name',
label: 'Last Name',
},
{
field: 'date',
label: 'Date',
centered: true
},
{
field: 'gender',
label: 'Gender',
}
]
}
}
}
</script>
# Sandbox with custom template
To show a custom row template, you should not use the columns
prop, but add it within the scoped slot as a component.
Also you can add an empty
named slot to show when there's no data.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 10/15/2016 | Male |
2 | John | Jacobs | 12/15/2016 | Male |
3 | Tina | Gilbert | 4/26/2016 | Female |
4 | Clarence | Flores | 4/10/2016 | Male |
5 | Anne | Lee | 12/6/2016 | Female |
Total | 2 Females, 3 Males |
<template>
<section>
<b-field grouped group-multiline>
<div class="control">
<b-switch v-model="isBordered">Bordered</b-switch>
</div>
<div class="control">
<b-switch v-model="isStriped">Striped</b-switch>
</div>
<div class="control">
<b-switch v-model="isNarrowed">Narrowed</b-switch>
</div>
<div class="control">
<b-switch v-model="isHoverable">Hoverable</b-switch>
</div>
<div class="control">
<b-switch v-model="isFocusable">Focusable</b-switch>
</div>
<div class="control">
<b-switch v-model="isLoading">Loading state</b-switch>
</div>
<div class="control">
<b-switch v-model="isEmpty">Empty</b-switch>
</div>
<div class="control">
<b-switch v-model="hasMobileCards">Mobile cards <small>(collapsed rows)</small></b-switch>
</div>
</b-field>
<b-table
:data="isEmpty ? [] : data"
:bordered="isBordered"
:striped="isStriped"
:narrowed="isNarrowed"
:hoverable="isHoverable"
:loading="isLoading"
:focusable="isFocusable"
:mobile-cards="hasMobileCards">
<b-table-column field="id" label="ID" width="40" :td-attrs="columnTdAttrs" numeric v-slot="props">
{{ props.row.id }}
</b-table-column>
<b-table-column field="first_name" label="First Name" :td-attrs="columnTdAttrs" v-slot="props">
{{ props.row.first_name }}
</b-table-column>
<b-table-column field="last_name" label="Last Name" :td-attrs="columnTdAttrs" v-slot="props">
{{ props.row.last_name }}
</b-table-column>
<b-table-column field="date" label="Date" :th-attrs="dateThAttrs" :td-attrs="columnTdAttrs" centered v-slot="props">
<span class="tag is-success">
{{ new Date(props.row.date).toLocaleDateString() }}
</span>
</b-table-column>
<b-table-column label="Gender" :td-attrs="columnTdAttrs" v-slot="props">
<span>
<b-icon
v-if="props.row.id !== 'Total'"
pack="fas"
:icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
</b-icon>
{{ props.row.gender }}
</span>
</b-table-column>
<template #empty>
<div class="has-text-centered">No records</div>
</template>
</b-table>
</section>
</template>
<script>
export default {
data() {
const data = [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016/10/15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016/12/15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016/04/26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016/04/10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016/12/06 14:38:38', 'gender': 'Female' },
{ 'id': 'Total', 'gender': '2 Females, 3 Males' },
]
return {
data,
isEmpty: false,
isBordered: false,
isStriped: false,
isNarrowed: false,
isHoverable: false,
isFocusable: false,
isLoading: false,
hasMobileCards: true
}
},
methods: {
dateThAttrs(column) {
return column.label === 'Date' ? {
title: 'This title is sponsored by "th-attrs" prop',
class: 'has-text-success'
} : null
},
columnTdAttrs(row, column) {
if (row.id === 'Total') {
if (column.label === 'ID') {
return {
colspan: 4,
class: 'has-text-weight-bold',
style: {
'text-align': 'left !important'
}
}
} else if (column.label === 'Gender') {
return {
class: 'has-text-weight-semibold'
}
} else {
return {
style: {display: 'none'}
}
}
}
return null
}
}
}
</script>
# Selection
You can show a single selected row by passing the corresponding object to the selected
prop. Additionally, adding the .sync
modifier will make it two-way binding — selected object will mutate if user clicks on row.
Use focusable
prop make table to be focused and navigable with a keyboard using arrow keys.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<section>
<b-field>
<b-button
label="Clear selected"
type="is-danger"
icon-left="close"
:disabled="!selected"
@click="selected = null" />
</b-field>
<b-tabs>
<b-tab-item label="Table">
<b-table
:data="data"
:columns="columns"
:selected.sync="selected"
focusable>
</b-table>
</b-tab-item>
<b-tab-item label="Selected">
<pre>{{ selected }}</pre>
</b-tab-item>
</b-tabs>
</section>
</template>
<script>
export default {
data() {
const data = [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
]
return {
data,
selected: data[1],
columns: [
{
field: 'id',
label: 'ID',
width: '40',
numeric: true
},
{
field: 'first_name',
label: 'First Name',
},
{
field: 'last_name',
label: 'Last Name',
},
{
field: 'date',
label: 'Date',
centered: true
},
{
field: 'gender',
label: 'Gender',
}
]
}
}
}
</script>
# Checkable
You can add checkboxes to rows by using the checkable
prop.
To show which rows are checked, you have to pass the corresponding object array to the checked-rows
prop. Adding the .sync
modifier will make it two-way binding — checked object array will mutate if user clicks on checkboxes.
A slot called bottom-left
is available whenever the table is checkable or paginated, you can add anything in there.
ID | First Name | Last Name | Date | Gender | |
---|---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male | |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male | |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female | |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male | |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<section>
<b-field grouped group-multiline>
<b-button
label="Clear checked"
type="is-danger"
icon-left="close"
class="field"
@click="checkedRows = []" />
<b-select v-model="checkboxPosition">
<option value="left">Checkbox at left</option>
<option value="right">Checkbox at right</option>
</b-select>
<b-select v-model="checkboxType">
<option value="is-primary">Default</option>
<option value="is-info">Info</option>
<option value="is-success">Success</option>
<option value="is-danger">Danger</option>
<option value="is-warning">Warning</option>
</b-select>
</b-field>
<b-tabs>
<b-tab-item label="Table">
<b-table
:data="data"
:columns="columns"
:checked-rows.sync="checkedRows"
:is-row-checkable="(row) => row.id !== 3 && row.id !== 4"
checkable
:checkbox-position="checkboxPosition"
:checkbox-type="checkboxType">
<template #bottom-left>
<b>Total checked</b>: {{ checkedRows.length }}
</template>
</b-table>
</b-tab-item>
<b-tab-item label="Checked rows">
<pre>{{ checkedRows }}</pre>
</b-tab-item>
</b-tabs>
</section>
</template>
<script>
export default {
data() {
const data = [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
]
return {
data,
checkboxPosition: 'left',
checkboxType: 'is-primary',
checkedRows: [data[1], data[3]],
columns: [
{
field: 'id',
label: 'ID',
width: '40',
numeric: true
},
{
field: 'first_name',
label: 'First Name',
},
{
field: 'last_name',
label: 'Last Name',
},
{
field: 'date',
label: 'Date',
centered: true
},
{
field: 'gender',
label: 'Gender',
}
]
}
}
}
</script>
# Searchable
You can add search filtering to rows by using the searchable
prop.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
You can debounce search filter to avoid multiple filtering when typing.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
You can also customize the search input using a scoped slot.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<section>
<b-table
:data="data"
:columns="columns">
</b-table>
<hr />
<p>You can debounce search filter to avoid multiple filtering when typing.</p>
<b-table
:data="data"
:columns="columns"
:debounce-search="1000">
</b-table>
<p>You can also customize the search input using a scoped slot.</p>
<b-table
:data="data">
<template v-for="column in columns">
<b-table-column :key="column.id" v-bind="column">
<template
v-if="column.searchable && !column.numeric"
#searchable="props">
<b-input
v-model="props.filters[props.column.field]"
placeholder="Search..."
icon="magnify"
size="is-small" />
</template>
<template v-slot="props">
{{ props.row[column.field] }}
</template>
</b-table-column>
</template>
</b-table>
</section>
</template>
<script>
export default {
data() {
return {
data: [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
],
columns: [
{
field: 'id',
label: 'ID',
width: '100',
numeric: true,
searchable: true,
},
{
field: 'first_name',
label: 'First Name',
searchable: true,
},
{
field: 'last_name',
label: 'Last Name',
searchable: true,
},
{
field: 'date',
label: 'Date',
centered: true
},
{
field: 'gender',
label: 'Gender',
}
]
}
}
}
</script>
# Pagination and sorting
To make a column sortable, add the sortable
prop on it and specify a field
name.
You can also use the default-sort
prop to determine the default sort column and order. The column must be sortable
to work.
The default-sort-direction
prop can be set to determine the default sort column direction on the first click.
A slot called bottom-left
is available whenever the table is checkable or paginated, you can add anything in there.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
30 | Alan | Mendoza | 4/22/2016 | Male |
57 | Alan | Edwards | 3/22/2017 | Male |
20 | Albert | Mendoza | 8/8/2016 | Male |
45 | Andrea | Marshall | 9/4/2016 | Female |
8 | Andrew | Greene | 11/20/2016 | Male |
<template>
<section>
<b-field grouped group-multiline>
<b-select v-model="defaultSortDirection">
<option value="asc">Default sort direction: ASC</option>
<option value="desc">Default sort direction: DESC</option>
</b-select>
<b-select v-model="perPage" :disabled="!isPaginated">
<option value="5">5 per page</option>
<option value="10">10 per page</option>
<option value="15">15 per page</option>
<option value="20">20 per page</option>
</b-select>
<div class="control">
<b-button
label="Set page to 2"
:disabled="!isPaginated"
@click="currentPage = 2" />
</div>
<div class="control is-flex">
<b-switch v-model="isPaginated">Paginated</b-switch>
</div>
<div class="control is-flex">
<b-switch v-model="isPaginationSimple" :disabled="!isPaginated">Simple pagination</b-switch>
</div>
<div class="control is-flex">
<b-switch v-model="isPaginationRounded" :disabled="!isPaginated">Rounded pagination</b-switch>
</div>
<b-select v-model="paginationPosition" :disabled="!isPaginated">
<option value="bottom">bottom pagination</option>
<option value="top">top pagination</option>
<option value="both">both</option>
</b-select>
<b-select v-model="sortIcon">
<option value="arrow-up">Arrow sort icon</option>
<option value="menu-up">Caret sort icon</option>
<option value="chevron-up">Chevron sort icon </option>
</b-select>
<b-select v-model="sortIconSize">
<option value="is-small">Small sort icon</option>
<option value="">Regular sort icon</option>
<option value="is-medium">Medium sort icon</option>
<option value="is-large">Large sort icon</option>
</b-select>
<b-select v-model="paginationOrder">
<option value="">default pagination order</option>
<option value="is-centered">is-centered pagination order</option>
<option value="is-right">is-right pagination order</option>
</b-select>
<div class="control is-flex">
<b-switch v-model="hasInput">Input</b-switch>
</div>
<b-select v-model="inputPosition">
<option value="">default input position</option>
<option value="is-input-right">is-input-right</option>
<option value="is-input-left">is-input-left</option>
</b-select>
<b-input type="number" placeholder="debounce (milliseconds)" v-model="inputDebounce" min="0"></b-input>
</b-field>
<b-table
:data="data"
:paginated="isPaginated"
:per-page="perPage"
:current-page.sync="currentPage"
:pagination-simple="isPaginationSimple"
:pagination-position="paginationPosition"
:default-sort-direction="defaultSortDirection"
:pagination-rounded="isPaginationRounded"
:sort-icon="sortIcon"
:sort-icon-size="sortIconSize"
default-sort="user.first_name"
aria-next-label="Next page"
aria-previous-label="Previous page"
aria-page-label="Page"
aria-current-label="Current page"
:page-input="hasInput"
:pagination-order="paginationOrder"
:page-input-position="inputPosition"
:debounce-page-input="inputDebounce">
<b-table-column field="id" label="ID" width="40" sortable numeric v-slot="props">
{{ props.row.id }}
</b-table-column>
<b-table-column field="user.first_name" label="First Name" sortable v-slot="props">
{{ props.row.user.first_name }}
</b-table-column>
<b-table-column field="user.last_name" label="Last Name" sortable v-slot="props">
{{ props.row.user.last_name }}
</b-table-column>
<b-table-column field="date" label="Date" sortable centered v-slot="props">
<span class="tag is-success">
{{ new Date(props.row.date).toLocaleDateString() }}
</span>
</b-table-column>
<b-table-column label="Gender" v-slot="props">
<span>
<b-icon pack="fas"
:icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
</b-icon>
{{ props.row.gender }}
</span>
</b-table-column>
</b-table>
</section>
</template>
<script>
const data = require('@/data/sample.json')
export default {
data() {
return {
data,
isPaginated: true,
isPaginationSimple: false,
isPaginationRounded: false,
paginationPosition: 'bottom',
defaultSortDirection: 'asc',
sortIcon: 'arrow-up',
sortIconSize: 'is-small',
currentPage: 1,
perPage: 5,
hasInput: false,
paginationOrder: '',
inputPosition: '',
inputDebounce: ''
}
}
}
</script>
# Sorting multiple
To sort on additional columns, use sort-multiple
is enabled
Use $refs.YOURREF.resetMultiSorting()
to reset the current multi column sorting
Use sort-multiple-data
prop together with backend-sorting
if you want to use a custom sorting priority
Use sort-multiple-key
prop if you only want to enable multi column sorting when it is in combination with a key. Use value null
to have it always enabled (default if not specified)
First name | Last name | Team |
---|---|---|
Abbie | Archer | Team B |
Abbie | Smith | Team A |
Jones | Smith | Team C |
Abbie | Archer | Team A |
<template>
<div>
<b-field grouped group-multiline>
<div class="control is-flex">
<b-switch v-model="multiColumnSortingEnabled" @input="resetPriority">Sort multiple rows</b-switch>
</div>
<div class="control is-flex">
<span class="button" :disabled="!multiColumnSortingEnabled" @click="resetPriority">Reset sorting</span>
</div>
<div class="control is-flex">
<b-select v-model="customKey" :disabled="!multiColumnSortingEnabled">
<option :value="null">None</option>
<option value="shiftKey">Shift</option>
<option value="altKey">Alt/Option</option>
<option value="ctrlKey">Control</option>
</b-select>
</div>
<div class="control is-flex">
<b-switch v-model="backendSortingEnabled" @input="resetPriority">Backend sorting</b-switch>
</div>
</b-field>
<b-table
:data="data"
ref="multiSortTable"
:backend-sorting="backendSortingEnabled"
@sort="sortPressed"
@sorting-priority-removed="sortingPriorityRemoved"
:sort-multiple="multiColumnSortingEnabled"
:sort-multiple-data="sortingPriority"
:sort-multiple-key="customKey"
>
<b-table-column field="first_name" label="First name" sortable v-slot="props">
{{ props.row.first_name }}
</b-table-column>
<b-table-column field="last_name" label="Last name" sortable v-slot="props">
{{ props.row.last_name }}
</b-table-column>
<b-table-column field="team" label="Team" sortable v-slot="props">
{{ props.row.team }}
</b-table-column>
</b-table>
</div>
</template>
<script>
import orderBy from 'lodash/orderBy'
const dataSource = [
{ 'id': 1, 'first_name': 'Abbie', 'last_name': 'Archer', 'team': 'Team B'},
{ 'id': 2, 'first_name': 'Abbie', 'last_name': 'Smith', 'team': 'Team A'},
{ 'id': 3, 'first_name': 'Jones', 'last_name': 'Smith', 'team': 'Team C'},
{ 'id': 4, 'first_name': 'Abbie', 'last_name': 'Archer', 'team': 'Team A'}
]
export default {
data() {
return {
customKey: null,
backendSortingEnabled: false,
multiColumnSortingEnabled: true,
data: [],
sortingPriority: []
}
},
methods: {
resetPriority(){
this.$refs.multiSortTable.resetMultiSorting()
// reset local backend sorting
if(this.backendSortingEnabled) {
this.sortingPriority = []
this.loadAsyncData()
}
},
// Backend sorting
sortingPriorityRemoved(field){
this.sortingPriority = this.sortingPriority.filter(
(priority) => priority.field !== field)
this.loadAsyncData(this.sortingPriority)
},
sortPressed(field, order, event) {
if(this.backendSortingEnabled) {
if(this.multiColumnSortingEnabled){
if((this.customKey && event[this.customKey]) || !this.customKey) {
let existingPriority = this.sortingPriority.filter(i => i.field === field)[0]
if(existingPriority) {
existingPriority.order = existingPriority.order === 'desc' ? 'asc' : 'desc'
} else {
// request sorted data from backend
this.sortingPriority.push({field, order})
}
this.loadAsyncData(this.sortingPriority)
} else {
// request regular sorted data from backend
this.sortingPriority = [] // [{field, order}]
this.loadAsyncData([{field, order}])
}
}
}
},
// "API request" for data
async loadAsyncData(sortingPriority = []) {
let mockdata = JSON.parse(JSON.stringify(dataSource));
// get data already sorted from the backend using sortingPriority
this.data = orderBy(mockdata, sortingPriority.map(i => i.field), sortingPriority.map(i => i.order))
}
},
created () {
this.data = JSON.parse(JSON.stringify(dataSource));
}
}
</script>
# Detailed rows
You can have detailed rows by adding a detail
named scoped slot and the detailed
prop.
You can also toggle row detail programmatically using toggleDetails
method by ref or by default slot and :show-detail-icon="false"
if you want to hide chevron icon.
<template>
<section>
<b-field grouped group-multiline>
<div class="control">
<b-switch v-model="showDetailIcon">Show detail icon</b-switch>
</div>
<div class="control">
<b-switch v-model="useTransition">Use transition (fade) when toggling details</b-switch>
</div>
</b-field>
<b-table
:data="data"
ref="table"
paginated
per-page="5"
:opened-detailed="defaultOpenedDetails"
detailed
detail-key="id"
:detail-transition="transitionName"
@details-open="(row) => $buefy.toast.open(`Expanded ${row.user.first_name}`)"
:show-detail-icon="showDetailIcon"
aria-next-label="Next page"
aria-previous-label="Previous page"
aria-page-label="Page"
aria-current-label="Current page">
<b-table-column field="id" label="ID" width="40" numeric v-slot="props">
{{ props.row.id }}
</b-table-column>
<b-table-column field="user.first_name" label="First Name" sortable v-slot="props">
<template v-if="showDetailIcon">
{{ props.row.user.first_name }}
</template>
<template v-else>
<a @click="props.toggleDetails(props.row)">
{{ props.row.user.first_name }}
</a>
</template>
</b-table-column>
<b-table-column field="user.last_name" label="Last Name" sortable v-slot="props">
{{ props.row.user.last_name }}
</b-table-column>
<b-table-column field="date" label="Date" sortable centered v-slot="props">
<span class="tag is-success">
{{ new Date(props.row.date).toLocaleDateString() }}
</span>
</b-table-column>
<b-table-column label="Gender" v-slot="props">
<span>
<b-icon pack="fas"
:icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
</b-icon>
{{ props.row.gender }}
</span>
</b-table-column>
<template #detail="props">
<article class="media">
<figure class="media-left">
<p class="image is-64x64">
<img src="/static/img/placeholder-128x128.png">
</p>
</figure>
<div class="media-content">
<div class="content">
<p>
<strong>{{ props.row.user.first_name }} {{ props.row.user.last_name }}</strong>
<small>@{{ props.row.user.first_name }}</small>
<small>31m</small>
<br>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
Maecenas non massa sem. Etiam finibus odio quis feugiat facilisis.
</p>
</div>
</div>
</article>
</template>
</b-table>
</section>
</template>
<script>
const data = require('@/data/sample.json')
export default {
data() {
return {
data,
defaultOpenedDetails: [1],
showDetailIcon: true,
useTransition: false
}
},
computed: {
transitionName() {
if (this.useTransition) {
return 'fade'
}
}
}
}
</script>
# Custom Detailed rows
You can add anything you like into the detail
named scoped by providing the customDetailRow
prop to the table.
<template>
<section>
<b-field grouped group-multiline>
<div class="control">
<b-checkbox v-model="showDetailIcon">Detail column</b-checkbox>
</div>
<div v-for="(column, index) in columnsVisible"
:key="index"
class="control">
<b-checkbox v-model="column.display">
{{ column.title }}
</b-checkbox>
</div>
</b-field>
<b-table
:data="data"
ref="table"
detailed
hoverable
custom-detail-row
:opened-detailed="['Board Games']"
:default-sort="['name', 'asc']"
detail-key="name"
@details-open="(row, index) => $buefy.toast.open(`Expanded ${row.name}`)"
:show-detail-icon="showDetailIcon">
<b-table-column
field="name"
:visible="columnsVisible['name'].display"
:label="columnsVisible['name'].title"
width="300"
sortable
v-slot="props"
>
<template v-if="showDetailIcon">
{{ props.row.name }}
</template>
<template v-else>
<a @click="toggle(props.row)">
{{ props.row.name }}
</a>
</template>
</b-table-column>
<b-table-column
field="sold"
:visible="columnsVisible['sold'].display"
:label="columnsVisible['sold'].title"
sortable
centered
v-slot="props"
>
{{ props.row.sold }}
</b-table-column>
<b-table-column
field="available"
:visible="columnsVisible['available'].display"
:label="columnsVisible['available'].title"
sortable
centered
v-slot="props"
>
{{ props.row.available }}
</b-table-column>
<b-table-column
:visible="columnsVisible['cleared'].display"
:label="columnsVisible['cleared'].title"
centered
v-slot="props"
>
<span :class="
[
'tag',
{'is-danger': props.row.sold / props.row.available <= 0.45},
{'is-success': props.row.sold / props.row.available > 0.45}
]">
{{ Math.round((props.row.sold / props.row.available) * 100) }}%
</span>
</b-table-column>
<template slot="detail" slot-scope="props">
<tr v-for="item in props.row.items" :key="item.name">
<td v-if="showDetailIcon"></td>
<td v-show="columnsVisible['name'].display" > {{ item.name }}</td>
<td v-show="columnsVisible['sold'].display" class="has-text-centered">{{ item.sold }}</td>
<td v-show="columnsVisible['available'].display" class="has-text-centered">{{ item.available }}</td>
<td v-show="columnsVisible['cleared'].display" class="has-text-centered">
<span :class="
[
'tag',
{'is-danger': item.sold / item.available <= 0.45},
{'is-success': item.sold / item.available > 0.45}
]">
{{ Math.round((item.sold / item.available) * 100) }}%
</span>
</td>
</tr>
</template>
</b-table>
</section>
</template>
<script>
export default {
data() {
return {
data: [
{
name: 'Board Games',
sold: 131,
available: 301,
items: [
{
name: 'Monopoly',
sold: 57,
available: 100
},
{
name: 'Scrabble',
sold: 23,
available: 84
},
{
name: 'Chess',
sold: 37,
available: 61
},
{
name: 'Battleships',
sold: 14,
available: 56
}
]
},
{
name: 'Jigsaws & Puzzles',
sold: 88,
available: 167,
items: [
{
name: 'World Map',
sold: 31,
available: 38
},
{
name: 'London',
sold: 23,
available: 29
},
{
name: 'Sharks',
sold: 20,
available: 44
},
{
name: 'Disney',
sold: 14,
available: 56
}
]
},
{
name: 'Books',
sold: 434,
available: 721,
items: [
{
name: 'Hamlet',
sold: 101,
available: 187
},
{
name: 'The Lord Of The Rings',
sold: 85,
available: 156
},
{
name: 'To Kill a Mockingbird',
sold: 78,
available: 131
},
{
name: 'Catch-22',
sold: 73,
available: 98
},
{
name: 'Frankenstein',
sold: 51,
available: 81
},
{
name: 'Alice\'s Adventures In Wonderland',
sold: 46,
available: 68
}
]
}
],
columnsVisible: {
name: { title: 'Name', display: true },
sold: { title: 'Stock Sold', display: true },
available: { title: 'Stock Available', display: true },
cleared: { title: 'Stock Cleared', display: true },
},
showDetailIcon: true
}
},
methods: {
toggle(row) {
this.$refs.table.toggleDetails(row)
}
}
}
</script>
# Row status
Use the row-class
prop to return a class name. It's a function that receives row
and index
parameters.
Note that you have to style the class yourself.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<b-table
:data="data"
:columns="columns"
:row-class="(row, index) => row.id === 1 && 'is-info'">
</b-table>
</template>
<script>
export default {
data() {
return {
data: [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
],
columns: [
{
field: 'id',
label: 'ID',
width: '40',
numeric: true
},
{
field: 'first_name',
label: 'First Name',
},
{
field: 'last_name',
label: 'Last Name',
},
{
field: 'date',
label: 'Date',
centered: true
},
{
field: 'gender',
label: 'Gender',
}
]
}
}
}
</script>
<style>
tr.is-info {
background: #167df0;
color: #fff;
}
</style>
# Custom headers
By adding a scoped slot named header
in table component you can customize the headers. Use the meta
prop on column to pass anything you may need.
Previous scoped slot named header
in table will be deprecated.
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 10/15/2016 | Male |
2 | John | Jacobs | 12/15/2016 | Male |
3 | Tina | Gilbert | 4/26/2016 | Female |
4 | Clarence | Flores | 4/10/2016 | Male |
5 | Anne | Lee | 12/6/2016 | Female |
<template>
<b-table :data="data">
<b-table-column field="id" label="ID" width="40" numeric>
<template v-slot:header="{ column }">
<b-tooltip :label="column.label" append-to-body dashed>
{{ column.label }}
</b-tooltip>
</template>
<template v-slot="props">
{{ props.row.id }}
</template>
</b-table-column>
<b-table-column field="user.first_name" label="First Name">
<template v-slot:header="{ column }">
<b-tooltip :label="column.label" append-to-body dashed>
{{ column.label }}
</b-tooltip>
</template>
<template v-slot="props">
{{ props.row.first_name }}
</template>
</b-table-column>
<b-table-column field="user.last_name" label="Last Name">
<template v-slot:header="{ column }">
<b-tooltip :label="column.label" append-to-body dashed>
{{ column.label }}
</b-tooltip>
</template>
<template v-slot="props">
{{ props.row.last_name }}
</template>
</b-table-column>
<b-table-column field="date" label="Date" centered v-slot="props">
<span class="tag is-success">
{{ new Date(props.row.date).toLocaleDateString() }}
</span>
</b-table-column>
<b-table-column label="Gender" v-slot="props">
<span>
<b-icon pack="fas"
:icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
</b-icon>
{{ props.row.gender }}
</span>
</b-table-column>
</b-table>
</template>
<script>
export default {
data() {
return {
data: [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
]
}
}
}
</script>
# Subheadings
Use the subheading
prop on columns to add subheadings. This is particularly useful to display a summary when dealing with long tables.
By adding a scoped slot named subheading
in table component you can customize the subheadings.
ID | Contributor | Posts | Comments |
---|---|---|---|
Total: | 18 | 60 | |
1 | Jesse Simmons | 2 | 5 |
2 | John Jacobs | 11 | 42 |
3 | Tina Gilbert | 0 | 7 |
4 | Clarence Flores | 4 | 4 |
5 | Anne Lee | 1 | 2 |
<template>
<section>
<b-table
:data="data"
:columns="columns">
</b-table>
</section>
</template>
<script>
export default {
data() {
return {
data: [
{ 'id': 1, 'contributor': 'Jesse Simmons', 'posts': 2, 'comments': 5 },
{ 'id': 2, 'contributor': 'John Jacobs', 'posts': 11, 'comments': 42 },
{ 'id': 3, 'contributor': 'Tina Gilbert', 'posts': 0, 'comments': 7 },
{ 'id': 4, 'contributor': 'Clarence Flores', 'posts': 4, 'comments': 4 },
{ 'id': 5, 'contributor': 'Anne Lee', 'posts': 1, 'comments': 2 }
],
columns: [
{
field: 'id',
label: 'ID',
width: '100',
numeric: true,
subheading: 'Total:'
},
{
field: 'contributor',
label: 'Contributor',
},
{
field: 'posts',
label: 'Posts',
subheading: 18
},
{
field: 'comments',
label: 'Comments',
subheading: 60
}
]
}
}
}
</script>
# Sticky Headers and Columns
Use the sticky-header
prop to show a scrolling table with fixed headers.
Use the sticky
prop on column to show a scrolling table with a fixed column.
The default height is 300px
but you can overwrite it using height
prop or $table-sticky-header-height
Sass variable.
ID | First Name | Last Name | Date | Gender | Column A | Column B | Column C | Column D | Column E | Column F | Column G | Column H | Column I | Column L | Column M | Column N | Column O |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Jesse | Simmons | 2016/10/15 13:43:27 | Male | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | John | Jacobs | 2016/12/15 06:00:53 | Male | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
3 | Tina | Gilbert | 2016/04/26 06:26:28 | Female | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
4 | Clarence | Flores | 2016/04/10 10:28:46 | Male | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
5 | Anne | Lee | 2016/12/06 14:38:38 | Female | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 |
6 | Sara | Armstrong | 2016/09/23 18:50:04 | Female | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
7 | Anthony | Webb | 2016/08/30 23:49:38 | Male | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 |
8 | Andrew | Greene | 2016/11/20 14:57:47 | Male | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 |
9 | Russell | White | 2016/07/13 09:29:49 | Male | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
10 | Lori | Hunter | 2016/12/09 01:44:05 | Female | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 |
checkable
and sticky-checkbox
to make a sticky checkbox column. ID | First Name | Last Name | Date | Gender | Column A | Column B | Column C | Column D | Column E | Column F | Column G | Column H | Column I | Column L | Column M | Column N | Column O | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Jesse | Simmons | 2016/10/15 13:43:27 | Male | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | |
2 | John | Jacobs | 2016/12/15 06:00:53 | Male | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | |
3 | Tina | Gilbert | 2016/04/26 06:26:28 | Female | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | |
4 | Clarence | Flores | 2016/04/10 10:28:46 | Male | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | |
5 | Anne | Lee | 2016/12/06 14:38:38 | Female | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | |
6 | Sara | Armstrong | 2016/09/23 18:50:04 | Female | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | |
7 | Anthony | Webb | 2016/08/30 23:49:38 | Male | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | |
8 | Andrew | Greene | 2016/11/20 14:57:47 | Male | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | |
9 | Russell | White | 2016/07/13 09:29:49 | Male | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | |
10 | Lori | Hunter | 2016/12/09 01:44:05 | Female | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 |
<template>
<section>
<b-field grouped group-multiline>
<div class="control">
<b-switch v-model="stickyHeaders">Sticky Headers</b-switch>
</div>
<div class="control">
<b-switch v-model="dateSearchable">Date searchable</b-switch>
</div>
</b-field>
<b-table
:data="data"
:columns="columns"
:sticky-header="stickyHeaders"
></b-table>
<br />
Use <code>checkable</code> and <code>sticky-checkbox</code> to make a sticky checkbox column.
<br />
<br />
<b-table
:data="data"
:columns="checkableColumns"
:sticky-header="stickyHeaders"
checkable
sticky-checkbox
striped
></b-table>
</section>
</template>
<script>
export default {
data() {
return {
data: [
{
id: 1,
user: { first_name: "Jesse", last_name: "Simmons" },
date: "2016/10/15 13:43:27",
gender: "Male"
},
{
id: 2,
user: { first_name: "John", last_name: "Jacobs" },
date: "2016/12/15 06:00:53",
gender: "Male"
},
{
id: 3,
user: { first_name: "Tina", last_name: "Gilbert" },
date: "2016/04/26 06:26:28",
gender: "Female"
},
{
id: 4,
user: { first_name: "Clarence", last_name: "Flores" },
date: "2016/04/10 10:28:46",
gender: "Male"
},
{
id: 5,
user: { first_name: "Anne", last_name: "Lee" },
date: "2016/12/06 14:38:38",
gender: "Female"
},
{
id: 6,
user: { first_name: "Sara", last_name: "Armstrong" },
date: "2016/09/23 18:50:04",
gender: "Female"
},
{
id: 7,
user: { first_name: "Anthony", last_name: "Webb" },
date: "2016/08/30 23:49:38",
gender: "Male"
},
{
id: 8,
user: { first_name: "Andrew", last_name: "Greene" },
date: "2016/11/20 14:57:47",
gender: "Male"
},
{
id: 9,
user: { first_name: "Russell", last_name: "White" },
date: "2016/07/13 09:29:49",
gender: "Male"
},
{
id: 10,
user: { first_name: "Lori", last_name: "Hunter" },
date: "2016/12/09 01:44:05",
gender: "Female"
}
],
stickyHeaders: true,
dateSearchable: false
};
},
computed: {
columns() {
return [
{
field: "id",
label: "ID",
width: "40",
numeric: true,
sticky: true,
headerClass: "is-sticky-column-one",
cellClass: "is-sticky-column-one"
},
{
field: "user.first_name",
label: "First Name"
},
{
field: "user.last_name",
label: "Last Name"
},
{
field: "date",
label: "Date",
searchable: this.dateSearchable,
centered: true,
sticky: true,
headerClass: "is-sticky-column-two",
cellClass: "is-sticky-column-two"
},
{
field: "gender",
label: "Gender"
},
{
field: "id",
label: "Column A"
},
{
field: "id",
label: "Column B"
},
{
field: "id",
label: "Column C"
},
{
field: "id",
label: "Column D"
},
{
field: "id",
label: "Column E"
},
{
field: "id",
label: "Column F"
},
{
field: "id",
label: "Column G"
},
{
field: "id",
label: "Column H"
},
{
field: "id",
label: "Column I"
},
{
field: "id",
label: "Column L"
},
{
field: "id",
label: "Column M"
},
{
field: "id",
label: "Column N"
},
{
field: "id",
label: "Column O"
}
];
},
checkableColumns() {
return [
{
field: "id",
label: "ID",
width: "40",
numeric: true,
sticky: false,
},
{
field: "user.first_name",
label: "First Name"
},
{
field: "user.last_name",
label: "Last Name"
},
{
field: "date",
label: "Date",
searchable: this.dateSearchable,
centered: true,
sticky: false,
},
{
field: "gender",
label: "Gender"
},
{
field: "id",
label: "Column A"
},
{
field: "id",
label: "Column B"
},
{
field: "id",
label: "Column C"
},
{
field: "id",
label: "Column D"
},
{
field: "id",
label: "Column E"
},
{
field: "id",
label: "Column F"
},
{
field: "id",
label: "Column G"
},
{
field: "id",
label: "Column H"
},
{
field: "id",
label: "Column I"
},
{
field: "id",
label: "Column L"
},
{
field: "id",
label: "Column M"
},
{
field: "id",
label: "Column N"
},
{
field: "id",
label: "Column O"
}
];
},
}
};
</script>
<style>
.is-sticky-column-one {
background: #23d160 !important;
color: white !important;
}
.is-sticky-column-two {
background: #167df0 !important;
color: white !important;
}
</style>
# Toggle columns
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<section>
<b-field grouped group-multiline>
<div v-for="(column, index) in columnsTemplate"
:key="index"
class="control">
<b-checkbox v-model="column.visible">
{{ column.title }}
</b-checkbox>
</div>
</b-field>
<b-table :data="tableDataSimple">
<b-table-column v-for="(column, index) in columnsTemplate"
:key="index"
:label="column.title"
:visible="column.visible"
v-slot="props">
{{ props.row[column.field] }}
</b-table-column>
</b-table>
</section>
</template>
<script>
export default {
data() {
const tableDataSimple = [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
]
return {
tableDataSimple,
columnsTemplate: [
{ title: 'ID', field: 'id', visible: true },
{ title: 'First Name', field: 'first_name', visible: true },
{ title: 'Last Name', field: 'last_name', visible: true },
{ title: 'Date', field: 'date', visible: true },
{ title: 'Gender', field: 'gender', visible: true }
]
}
}
}
</script>
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 10/15/2016 | Male |
2 | John | Jacobs | 12/15/2016 | Male |
3 | Tina | Gilbert | 4/26/2016 | Female |
4 | Clarence | Flores | 4/10/2016 | Male |
5 | Anne | Lee | 12/6/2016 | Female |
Footer |
<template>
<section>
<b-field grouped group-multiline>
<div class="control">
<b-switch v-model="isCustom">Custom</b-switch>
</div>
</b-field>
<b-table :data="tableDataSimple">
<b-table-column label="ID" width="40" numeric v-slot="props">
{{ props.row.id }}
</b-table-column>
<b-table-column label="First Name" v-slot="props">
{{ props.row.first_name }}
</b-table-column>
<b-table-column label="Last Name" v-slot="props">
{{ props.row.last_name }}
</b-table-column>
<b-table-column label="Date" centered v-slot="props">
{{ new Date(props.row.date).toLocaleDateString() }}
</b-table-column>
<b-table-column label="Gender" v-slot="props">
{{ props.row.gender }}
</b-table-column>
<template #footer v-if="!isCustom">
<div class="has-text-right">
Footer
</div>
</template>
<template #footer v-else>
<th class="is-hidden-mobile" style="width:40px">
<div class="th-wrap is-numeric"> ID </div>
</th>
<th class="is-hidden-mobile">
<div class="th-wrap"> First Name </div>
</th>
<th class="is-hidden-mobile">
<div class="th-wrap"> Last Name </div>
</th>
<th class="is-hidden-mobile">
<div class="th-wrap is-centered"> Date </div>
</th>
<th class="is-hidden-mobile">
<div class="th-wrap"> Gender </div>
</th>
</template>
</b-table>
</section>
</template>
<script>
export default {
data() {
const tableDataSimple = [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016/10/15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016/12/15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016/04/26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016/04/10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016/12/06 14:38:38', 'gender': 'Female' }
]
return {
tableDataSimple,
isCustom: false
}
}
}
</script>
# Async data
Use backend-sorting
and backend-pagination
props to let those tasks to the backend, then manage it with page-change
and sort
events.
API from TMDb.
Title | Vote Average | Vote Count | Release Date | Overview |
---|---|---|---|---|
Inception | 8.367 | 35553 | 7/15/2010 | Cobb, a skilled thief who commits corporate espionage by infiltrating the subcon... |
Interstellar | 8.433 | 34084 | 11/5/2014 | The adventures of a group of explorers who make use of a newly discovered wormho... |
The Dark Knight | 8.515 | 31771 | 7/16/2008 | Batman raises the stakes in his war on crime. With the help of Lt. Jim Gordon an... |
Avatar | 7.581 | 30729 | 12/15/2009 | In the 22nd century, a paraplegic Marine is dispatched to the moon Pandora on a ... |
The Avengers | 7.714 | 29934 | 4/25/2012 | When an unexpected enemy emerges and threatens global safety and security, Nick ... |
Deadpool | 7.611 | 29735 | 2/9/2016 | The origin story of former Special Forces operative turned mercenary Wade Wilson... |
Avengers: Infinity War | 8.248 | 28757 | 4/25/2018 | As the Avengers and their allies have continued to protect the world from threat... |
Fight Club | 8.441 | 28361 | 10/15/1999 | A ticking-time-bomb insomniac and a slippery soap salesman channel primal male a... |
Guardians of the Galaxy | 7.908 | 27332 | 7/30/2014 | Light years from Earth, 26 years after being abducted, Peter Quill finds himself... |
Pulp Fiction | 8.5 | 26939 | 9/10/1994 | A burger-loving hit man, his philosophical partner, a drug-addled gangster's mol... |
Forrest Gump | 8.475 | 26463 | 6/23/1994 | A man with a low IQ has accomplished great things in his life and been present d... |
Harry Potter and the Philosopher's Stone | 7.913 | 26434 | 11/16/2001 | Harry Potter has lived under the stairs at his aunt and uncle's house his whole ... |
The Shawshank Redemption | 8.704 | 25989 | 9/23/1994 | Framed in the 1940s for the double murder of his wife and her lover, upstanding ... |
Iron Man | 7.643 | 25638 | 4/30/2008 | After being held captive in an Afghan cave, billionaire engineer Tony Stark crea... |
Django Unchained | 8.178 | 25479 | 12/25/2012 | With the help of a German bounty hunter, a freed slave sets out to rescue his wi... |
Avengers: Endgame | 8.256 | 24746 | 4/24/2019 | After the devastating events of Avengers: Infinity War, the universe is in ruins... |
The Matrix | 8.214 | 24728 | 3/31/1999 | Set in the 22nd century, The Matrix tells the story of a computer hacker who joi... |
Titanic | 7.905 | 24521 | 11/18/1997 | 101-year-old Rose DeWitt Bukater tells the story of her life aboard the Titanic,... |
Joker | 8.162 | 24406 | 10/1/2019 | During the 1980s, a failed stand-up comedian is driven insane and turns to a lif... |
The Lord of the Rings: The Fellowship of the Ring | 8.412 | 24215 | 12/18/2001 | Young hobbit Frodo Baggins, after inheriting a mysterious ring from his uncle Bi... |
<template>
<section>
<b-table
:data="data"
:loading="loading"
paginated
backend-pagination
:total="total"
:per-page="perPage"
@page-change="onPageChange"
aria-next-label="Next page"
aria-previous-label="Previous page"
aria-page-label="Page"
aria-current-label="Current page"
backend-sorting
:default-sort-direction="defaultSortOrder"
:default-sort="[sortField, sortOrder]"
@sort="onSort">
<b-table-column field="original_title" label="Title" sortable v-slot="props">
{{ props.row.original_title }}
</b-table-column>
<b-table-column field="vote_average" label="Vote Average" numeric sortable v-slot="props">
<span class="tag" :class="type(props.row.vote_average)">
{{ props.row.vote_average }}
</span>
</b-table-column>
<b-table-column field="vote_count" label="Vote Count" numeric sortable v-slot="props">
{{ props.row.vote_count }}
</b-table-column>
<b-table-column field="release_date" label="Release Date" sortable centered v-slot="props">
{{ props.row.release_date ? new Date(props.row.release_date).toLocaleDateString() : 'unknown' }}
</b-table-column>
<b-table-column label="Overview" width="500" v-slot="props">
{{ props.row.overview | truncate(80) }}
</b-table-column>
</b-table>
</section>
</template>
<script>
export default {
data() {
return {
data: [],
total: 0,
loading: false,
sortField: 'vote_count',
sortOrder: 'desc',
defaultSortOrder: 'desc',
page: 1,
perPage: 20
}
},
methods: {
/*
* Load async data
*/
loadAsyncData() {
const params = [
'api_key=bb6f51bef07465653c3e553d6ab161a8',
'language=en-US',
'include_adult=false',
'include_video=false',
`sort_by=${this.sortField}.${this.sortOrder}`,
`page=${this.page}`
].join('&')
this.loading = true
this.$http.get(`https://api.themoviedb.org/3/discover/movie?${params}`)
.then(({ data }) => {
// api.themoviedb.org manage max 1000 pages
this.data = []
let currentTotal = data.total_results
if (data.total_results / this.perPage > 1000) {
currentTotal = this.perPage * 1000
}
this.total = currentTotal
data.results.forEach((item) => {
item.release_date = item.release_date ? item.release_date.replace(/-/g, '/') : null
this.data.push(item)
})
this.loading = false
})
.catch((error) => {
this.data = []
this.total = 0
this.loading = false
throw error
})
},
/*
* Handle page-change event
*/
onPageChange(page) {
this.page = page
this.loadAsyncData()
},
/*
* Handle sort event
*/
onSort(field, order) {
this.sortField = field
this.sortOrder = order
this.loadAsyncData()
},
/*
* Type style in relation to the value
*/
type(value) {
const number = parseFloat(value)
if (number < 6) {
return 'is-danger'
} else if (number >= 6 && number < 8) {
return 'is-warning'
} else if (number >= 8) {
return 'is-success'
}
}
},
filters: {
/**
* Filter to truncate string, accepts a length parameter
*/
truncate(value, length) {
return value.length > length
? value.substr(0, length) + '...'
: value
}
},
mounted() {
this.loadAsyncData()
}
}
</script>
# Draggable rows/columns
Use draggable
/draggable-column
prop to allow rows and columns to be draggable. Manage dragging using dragstart
/columndragstart
, dragover
/columndragover
and drop
/columndrop
events
ID | First Name | Last Name | Date | Gender |
---|---|---|---|---|
1 | Jesse | Simmons | 2016-10-15 13:43:27 | Male |
2 | John | Jacobs | 2016-12-15 06:00:53 | Male |
3 | Tina | Gilbert | 2016-04-26 06:26:28 | Female |
4 | Clarence | Flores | 2016-04-10 10:28:46 | Male |
5 | Anne | Lee | 2016-12-06 14:38:38 | Female |
<template>
<div>
<b-table
:data="data"
:columns="columns"
draggable
draggable-column
@dragstart="dragstart"
@drop="drop"
@dragover="dragover"
@dragleave="dragleave"
@columndragstart="columndragstart"
@columndrop="columndrop"
@columndragover="columndragover"
@columndragleave="columndragleave">
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
data: [
{ 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
{ 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
{ 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
{ 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
{ 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
],
columns: [
{
field: 'id',
label: 'ID',
width: '40',
numeric: true
},
{
field: 'first_name',
label: 'First Name',
},
{
field: 'last_name',
label: 'Last Name',
},
{
field: 'date',
label: 'Date',
centered: true
},
{
field: 'gender',
label: 'Gender',
}
],
draggingRow: null,
draggingRowIndex: null,
draggingColumn: null,
draggingColumnIndex: null
}
},
methods: {
dragstart(payload) {
this.draggingRow = payload.row
this.draggingRowIndex = payload.index
payload.event.dataTransfer.effectAllowed = 'copy'
},
dragover(payload) {
payload.event.dataTransfer.dropEffect = 'copy'
payload.event.target.closest('tr').classList.add('is-selected')
payload.event.preventDefault()
},
dragleave(payload) {
payload.event.target.closest('tr').classList.remove('is-selected')
payload.event.preventDefault()
},
drop(payload) {
payload.event.target.closest('tr').classList.remove('is-selected')
const droppedOnRowIndex = payload.index
this.$buefy.toast.open(`Moved ${this.draggingRow.first_name} from row ${this.draggingRowIndex + 1} to ${droppedOnRowIndex + 1}`)
},
columndragstart(payload) {
this.draggingColumn = payload.column
this.draggingColumnIndex = payload.index
payload.event.dataTransfer.effectAllowed = 'copy'
},
columndragover(payload) {
payload.event.dataTransfer.dropEffect = 'copy'
payload.event.target.closest('th').classList.add('is-selected')
payload.event.preventDefault()
},
columndragleave(payload) {
payload.event.target.closest('th').classList.remove('is-selected')
payload.event.preventDefault()
},
columndrop(payload) {
payload.event.target.closest('th').classList.remove('is-selected')
const droppedOnColumnIndex = payload.index
this.$buefy.toast.open(`Moved ${this.draggingColumn.field} from column ${this.draggingColumnIndex + 1} to ${droppedOnColumnIndex + 1}`)
}
}
}
</script>
Name | Description | Type | Values | Default |
---|---|---|---|---|
data | Table data | Array | — | — |
columns | Table columns | Array | — | — |
default-sort | Sets the default sort column and order — e.g. ['first_name', 'desc'] | String, Array | — | order: default-sort-direction prop |
default-sort-direction | Sets the default sort column direction on the first click | String | asc , desc | asc |
sort-icon | Sets the header sorting icon | String | - | arrow-up |
sort-icon-size | Sets the size of the sorting icon | String | is-small , , is-medium , is-large | is-small |
bordered | Border to all cells | Boolean | — | false |
striped | Whether table is striped | Boolean | — | false |
narrowed | Makes the cells narrower | Boolean | — | false |
selected | Set which row is selected, use the .sync modifier to make it two-way binding | Object | — | — |
focusable | Table can be focused and user can navigate with keyboard arrows (require selected.sync ) and rows are highlighted when hovering | Boolean | — | false |
hoverable | Rows are highlighted when hovering | Boolean | — | false |
checkable | Rows can be checked (multiple), checked rows will have a .is-checked class if you want to style | Boolean | — | false |
checkbox-position | Position of the checkbox (if checkable is true) | String | left or right | left |
sticky-checkbox | Make the checkbox column sticky when checkable | Boolean | — | false |
checked-rows | Set which rows are checked, use the .sync modifier to make it two-way binding | Array | — | — |
header-checkable | Show check/uncheck all checkbox in table header when checkable | Boolean | — | true |
checkbox-type | Type (color) of the checkbox when checkable , optional | String | is-white , is-black , is-light ,
is-dark , is-primary , is-info , is-success ,
is-warning , is-danger ,
and any other colors you've set in the $colors list on Sass | is-primary |
mobile-cards | Rows appears as cards on mobile (collapse rows) | Boolean | — | true |
backend-sorting | Columns won't be sorted with Javascript, use with sort event to sort in your backend | Boolean | — | false |
backend-pagination | Rows won't be paginated with Javascript, use with page-change event to paginate in your backend | Boolean | — | false |
total | Total number of table data if backend-pagination is enabled | Number | — | 0 |
current-page | Current page of table data (if paginated ) | Number | — | 1 |
loading | Loading state | Boolean | — | false |
paginated | Adds pagination to the table | Boolean | — | false |
pagination-simple | Simple pagination (if paginated ) | Boolean | — | false |
pagination-rounded | Rounded pagination (if paginated ) | Boolean | — | false |
pagination-order | Buttons order, optional | String | is-centered , is-right | — |
pagination-size | Pagination size (if paginated ) | String | is-small , is-medium , is-large | — |
pagination-position | Pagination position (if paginated ) | String | bottom , top , both | bottom |
per-page | How many rows per page (if paginated ) | Number | — | 20 |
page-input | Include page number input. | Boolean | — | false |
page-input-position | Page input position. | String | is-input-right , is-input-left | — |
debounce-page-input | Sets the page input debounce time (in milliseconds) | Number | — | — |
sort-multiple | Adds multiple column sorting | Boolean | — | false |
sort-multiple-data | Used in combination with backend-sorting | Object | [{field, order}] | [] |
sort-multiple-key | Adds a key which will be required for multi column sorting to work. Will always be enabled if null is selected (default). Requires sort-multiple | String | null , shiftKey , altKey , ctrlKey | null |
row-class | Add a class to row (<tr> element) based on the return | Function (row: Object, index: Number) | — | — |
detailed | Allow row details (check scoped slots documentation) | Boolean | — | false |
custom-detail-row | Allow a custom detail row | Boolean | — | false |
show-detail-icon | Allow chevron icon and column to be visible | Boolean | — | true |
detail-icon | Icon name | String | — | chevron-right |
opened-detailed | Allow pre-defined opened details. Ideal to open details via vue-router. (A unique key is required; check detail-key prop) | Array | — | [] |
has-detailed-visible | Controls the visibility of the trigger that toggles the detailed rows. | Function (row: Object) | — | true |
detail-key | Use a unique key of your data Object when use detailed or opened detailed. (id recommended) | String | — | — |
detail-transition | Transition name to use when toggling row details. | String | — | — |
custom-is-checked | Custom method to verify if row is checked, works when is checkable . Useful for backend pagination | Function (a: Object, b: Object) | — | — |
is-row-checkable | Custom method to verify if a row is checkable, works when is checkable . | Function (row: Object) | — | true |
is-row-selectable | Custom method to verify if a row is selectable, works when is selected . | Function (row: Object) | — | true |
icon-pack | Icon pack to use | String | mdi , fa , fas , far , fad , fal | mdi |
mobile-sort-placeholder | Text when nothing is selected | String | — | — |
custom-row-key | Use a unique key of your data Object for each row. Useful if your data prop has dynamic indices. (id recommended) | String | - | - |
draggable | Allows rows to be draggable | Boolean | — | false |
draggable-column | Allows columns to be draggable | Boolean | — | false |
backend-filtering | Columns won't be filtered with Javascript, use with searchable prop to the columns to filter in your backend | Boolean | — | false |
sticky-header | Show a sticky table header | Boolean | — | false |
scrollable | Add a horizontal scrollbar when table is too wide | Boolean | — | false |
height | Table fixed height in pixels | Number, String | — | — |
filters-event | Add a native event to filter | String | — | — |
card-layout | Rows appears as cards (collapse rows) | Boolean | — | false |
show-header | Show table column header | Boolean | — | true |
aria-next-label | Accessibility label for the next page link (if paginated ) | String | — | — |
aria-previous-label | Accessibility label for the previous page link (if paginated ) | String | — | — |
aria-page-label | Accessibility label for the page link. If passed, this text will be prepended to the number of the page (if paginated ) | String | — | — |
aria-current-label | Accessibility label for the current page link. If passed, this text will be prepended to the current page (if paginated ) | String | — | — |
debounce-search | Sets the filtering debounce time (in milliseconds) | Number | — | — |
Name | Description | Type | Values | Default |
---|---|---|---|---|
label | Column header text, also used to identify column if custom-key prop is missing | String | — | — |
custom-key | Unique identifier, use when label is missing or there are duplicate label names | String, Number | — | this.label |
field | Property of the object the column is attributed, used for sorting | String | — | — |
meta | Meta prop to add anything, useful when creating custom headers | Any | — | — |
width | Column fixed width in any unit, or pixels when none is provided | Number, String | — | — |
numeric | Align the cell content to the right, sort icon on left | Boolean | — | false |
centered | Align the cell content to the center | Boolean | — | false |
sortable | Whether the column can be sorted | Boolean | — | false |
visible | Whether the column is visible | Boolean | — | true |
custom-sort | Custom sort method, works when column is sortable | Function (a: Object, b: Object, isAsc: Boolean) | — | — |
searchable | Add a input below the header to filter data | Boolean | — | false |
custom-search | Custom search method, works when column is searchable | Function (a: Object, input: String) | — | — |
subheading | Column subheading text | String, Number | — | — |
sticky | Show a sticky column | Boolean | — | false |
header-selectable | Whether the header text is selectable, works when column is sortable . | Boolean | — | false |
header-class | CSS classes to be applied on header | String | — | - |
cell-class | CSS classes to be applied on cell | String | — | - |
th-attrs | Adds native attributes to th :th-attrs="(column)" => ({})" | Function | — | - |
td-attrs | Adds native attributes to td :td-attrs="(row, column)" => ({})" | Function | — | - |
You can use these variables to customize this component.
Name | Default |
---|---|
$table-sticky-header-height | 300px |
Bulma variables | Link |
This page is open source. Noticed a typo or something's unclear?