this PR is an attempt to implement the feature mentioned the in issue https://github.com/cdmoro/bootstrap-vue-3/issues/673
I've tried my best to bring this feature on but not quite sure if did it right.
Note that the provider is now a standalone prop not as it used to be in BSVue2 and should be passed through the provider
prop instead of the items
prop
fully working example:
<template>
<b-container class="py-5">
<!-- User Interface controls -->
<b-row>
<b-col lg="6" class="my-1">
<b-form-group
v-slot="{ariaDescribedby}"
label="Sort"
label-for="sort-by-select"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="mb-0"
>
<b-input-group size="sm">
<b-form-select
id="sort-by-select"
v-model="sortBy"
:options="sortOptions"
:aria-describedby="ariaDescribedby"
class="w-75"
>
<template #first>
<option value="">-- none --</option>
</template>
</b-form-select>
<b-form-select
v-model="sortDesc"
:disabled="!sortBy"
:aria-describedby="ariaDescribedby"
size="sm"
class="w-25"
>
<option :value="false">Asc</option>
<option :value="true">Desc</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col lg="6" class="my-1">
<b-form-group
label="Filter"
label-for="filter-input"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="mb-0"
>
<b-input-group size="sm">
<b-form-input
id="filter-input"
v-model="filter"
type="search"
placeholder="Type to Search"
/>
<b-input-group-append>
<b-button :disabled="!filter" @click="filter = ''">Clear</b-button>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-col>
<b-col lg="6" class="my-1">
<b-form-group
v-slot="{ariaDescribedby}"
v-model="sortDirection"
label="Filter On"
description="Leave all unchecked to filter on all data"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="mb-0"
>
<div class="d-flex gap-2">
<b-form-checkbox v-model="filterOn" value="name">Name</b-form-checkbox>
<b-form-checkbox v-model="filterOn" value="age">Age</b-form-checkbox>
<b-form-checkbox v-model="filterOn" value="isActive">Active</b-form-checkbox>
</div>
</b-form-group>
</b-col>
<b-col sm="5" md="6" class="my-1">
<b-form-group
label="Per page"
label-for="per-page-select"
label-cols-sm="6"
label-cols-md="4"
label-cols-lg="3"
label-align-sm="right"
label-size="sm"
class="mb-0"
>
<b-form-select id="per-page-select" v-model="perPage" :options="pageOptions" size="sm" />
</b-form-group>
</b-col>
<b-col sm="7" md="6" class="my-1">
<b-pagination
v-model="currentPage"
:total-rows="totalRows"
:per-page="perPage"
align="fill"
size="sm"
class="my-0"
/>
</b-col>
</b-row>
<!-- Main table element -->
<b-table
v-model:sort-by="sortBy"
v-model:sort-desc="sortDesc"
v-model:busy="busy"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
responsive
:filterable="filterOn"
small
:sort-internal="true"
:provider="myProvider"
:no-providing="['paging']"
@filtered="onFiltered"
>
<template #cell(name)="row"> {{ row.value.first }} {{ row.value.last }} </template>
<template #cell(actions)="row">
<b-button size="sm" class="mr-1" @click="info(row.item, row.index, $event.target)">
Info modal
</b-button>
<b-button size="sm" @click="row.toggleDetails">
{{ row.detailsShowing ? 'Hide' : 'Show' }} Details
</b-button>
</template>
<template #row-details="row">
<b-card>
<ul>
<li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value }}</li>
<b-button size="sm" @click="row.toggleDetails"> Toggle Details </b-button>
</ul>
</b-card>
</template>
</b-table>
<!-- Info modal -->
<b-modal
:id="infoModal.id"
v-model="infoModal.open"
:title="infoModal.title"
ok-only
@hide="resetInfoModal"
>
<pre>{{ infoModal.content }}</pre>
</b-modal>
</b-container>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { ColorVariant, TableItem } from './types'
import { BTableProviderContext } from './types/components'
export default defineComponent({
data() {
return {
items: [
{isActive: true, age: 40, name: {first: 'Dickerson', last: 'Macdonald'}},
{isActive: false, age: 21, name: {first: 'Larsen', last: 'Shaw'}},
{
isActive: false,
age: 9,
name: {first: 'Mini', last: 'Navarro'},
_rowVariant: 'success' as ColorVariant,
},
{isActive: false, age: 89, name: {first: 'Geneva', last: 'Wilson'}},
{isActive: true, age: 38, name: {first: 'Jami', last: 'Carney'}},
{isActive: false, age: 27, name: {first: 'Essie', last: 'Dunlap'}},
{isActive: true, age: 40, name: {first: 'Thor', last: 'Macdonald'}},
{
isActive: true,
age: 87,
name: {first: 'Larsen', last: 'Shaw'},
_cellVariants: {age: 'danger', isActive: 'warning'} as Record<any, any>,
},
{isActive: false, age: 26, name: {first: 'Mitzi', last: 'Navarro'}},
{isActive: false, age: 22, name: {first: 'Genevieve', last: 'Wilson'}},
{isActive: true, age: 38, name: {first: 'John', last: 'Carney'}},
{isActive: false, age: 29, name: {first: 'Dick', last: 'Dunlap'}},
],
fields: [
{key: 'name', label: 'Person full name', sortable: true, sortDirection: 'desc'},
{key: 'age', label: 'Person age', sortable: true, class: 'text-center'},
{
key: 'isActive',
label: 'Is Active',
formatter: (value: any, key: any, item: any) => (value ? 'Yes' : 'No'),
sortable: true,
sortByFormatted: true,
filterByFormatted: true,
},
{key: 'actions', label: 'Actions'},
],
totalRows: 1,
currentPage: 1,
busy: false,
perPage: 5,
pageOptions: [5, 10, 15, {value: 100, text: 'Show a lot'}],
sortBy: '',
sortDesc: false,
sortDirection: 'asc',
filter: '',
filterOn: [],
infoModal: {
open: false,
id: 'info-modal',
title: '',
content: '',
},
}
},
computed: {
sortOptions() {
// Create an options list from our fields
return this.fields.filter((f) => f.sortable).map((f) => ({text: f.label, value: f.key}))
},
},
mounted() {
// Set the initial number of items
this.totalRows = this.items.length
},
methods: {
myProvider(
ctx: BTableProviderContext,
callback: (items: Array<TableItem>) => Promise<TableItem[] | undefined>
) {
//ctx: {
// filter?: string
// currentPage?: number
// perPage?: number
// sortBy?: string
// sortDesc?: Booleanish
// }
const items: Array<TableItem> = [
{isActive: false, age: 89, name: {first: 'Geneva', last: 'Wilson'}},
{isActive: true, age: 38, name: {first: 'Jami', last: 'Carney'}},
{isActive: false, age: 27, name: {first: 'Essie', last: 'Dunlap'}},
{isActive: true, age: 40, name: {first: 'Thor', last: 'Macdonald'}},
]
// console.log('Items set through a return', items);
// // You can return an arroy of items
// return items;
// also you can return undefined and set the items through the callback function
// callback(items)
// .then((items) => {
// console.log('Items set through callback function', items);
// })
// .finally(() => this.busy = false);
// return undefined;
// and finally you can return a promise that resolves an array of array OR undefined with setting the items through the callback function
return new Promise<Array<TableItem> | undefined>((resolve, reject) => {
setTimeout(() => {
resolve(items)
}, 3000)
})
},
info(item: any, index: any, button: any) {
this.infoModal.title = `Row index: ${index}`
this.infoModal.content = JSON.stringify(item, null, 2)
this.infoModal.open = true
},
resetInfoModal() {
this.infoModal.title = ''
this.infoModal.content = ''
},
onFiltered(filteredItems: any) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
},
},
})
</script>