仅在应用过滤器时显示 bootstrap-vue b-table 中的项目
Posted
技术标签:
【中文标题】仅在应用过滤器时显示 bootstrap-vue b-table 中的项目【英文标题】:Only display items in bootstrap-vue b-table when filter is applied 【发布时间】:2020-09-02 19:39:41 【问题描述】:当用户应用了过滤器(在输入中输入了一个值)时,有没有办法只在 bootstrap-vue b-tables 上显示项目?例如。如果“filteredItems”不存在,什么都不显示?这主要是为了防止我的表渲染所有行(> 2k行)并妨碍性能。
jsfiddle 与 b 表:https://jsfiddle.net/asc82spc/
const template = `
<table :id="id || null"
role="grid"
:aria-busy="isBusy ? 'true' : 'false'"
:class="tableClass"
>
<thead :class="headVariant ? ('thead-' + headVariant) : ''">
<tr role="row">
<th v-for="field,key in fields"
@click="headClick($event,field,key)"
@keydown.enter="headClick($event,field,key)"
@keydown.space.prevent="headClick($event,field,key)"
:class="fieldClass(field,key)"
:aria-label="field.sortable ? (sortDesc ? labelSortAsc : labelSortDesc) : null"
:aria-sort="(field.sortable && sortBy === key) ? (sortDesc ? 'descending' : 'ascending') : null"
:tabindex="field.sortable?'0':null"
v-html="field.label"
></th>
</tr>
</thead>
<tfoot v-if="footClone" :class="footVariant ? ('thead-' + footVariant) : ''">
<tr role="row">
<th v-for="field,key in fields"
@click="headClick($event,field,key)"
@keydown.enter="headClick($event,field,key)"
@keydown.space.prevent="headClick($event,field,key)"
:key="key"
:class="fieldClass(field,key)"
:aria-label="field.sortable ? ((sortDesc) ? labelSortAsc : labelSortDesc) : null"
:aria-sort="(field.sortable && sortBy === key) ? (sortDesc ? 'descending' : 'ascending') : null"
:tabindex="field.sortable?'0':null"
v-html="field.label"
></th>
</tr>
</tfoot>
<tbody>
<tr v-for="(item,index) in _items"
role="row"
:key="items_key"
:class="rowClass(item)"
@click="rowClicked($event,item,index)"
>
<td v-for="(field,key) in fields" :class="cellClass(field)">
<slot :name="key" :value="item[key]" :item="item" :index="index">item[key]</slot>
</td>
</tr>
<tr v-if="showEmpty && _items.length === 0" role="row">
<td :colspan="Object.keys(fields).length">
<div v-if="filter" role="alert" aria-live="polite">
<slot name="emptyfiltered">
<div class="text-center" v-html="emptyFilteredText"></div>
</slot>
</div>
<div v-else role="alert" aria-live="polite">
<slot name="empty">
<div class="text-center" v-html="emptyText"></div>
</slot>
</div>
</td>
</tr>
</tbody>
</table>
`;
const toString = v =>
if (!v)
return '';
if (v instanceof Object)
return Object.keys(v).map(k => toString(v[k])).join(' ');
return String(v);
;
const recToString = v =>
if (!(v instanceof Object))
return '';
// Exclude these fields from record stringification
const exclude =
state: true,
_rowVariant: true
;
return toString(Object.keys(v).filter(k => !exclude[k]).reduce((o, k) =>
o[k] = v[k];
return o;
, ));
;
const defaultSortCompare = (a, b, sortBy) =>
return toString(a[sortBy]).localeCompare(toString(b[sortBy]), undefined,
numeric: true
);
;
const bTable =
template: template,
data()
return
sortBy: null,
sortDesc: true,
isBusy: false,
localItems: null
;
,
props:
id:
type: String,
default: ''
,
items:
type: Array,
default: () => []
,
fields:
type: Object,
default: () =>
return ;
,
striped:
type: Boolean,
default: false
,
bordered:
type: Boolean,
default: false
,
inverse:
type: Boolean,
default: false
,
hover:
type: Boolean,
default: false
,
small:
type: Boolean,
default: false
,
responsive:
type: Boolean,
default: false
,
headVariant:
type: String,
default: ''
,
footVariant:
type: String,
default: ''
,
perPage:
type: Number,
default: null
,
items_key:
type: String,
default: null
,
currentPage:
type: Number,
default: 1
,
filter:
type: [String, RegExp, Function],
default: null
,
sortCompare:
type: Function,
default: null
,
itemsProvider:
type: Function,
default: null
,
noProviderPaging:
type: Boolean,
default: false
,
noProviderSorting:
type: Boolean,
default: false
,
noProviderFiltering:
type: Boolean,
default: false
,
value:
type: Array,
default: () => []
,
footClone:
type: Boolean,
default: false
,
labelSortAsc:
type: String,
default: 'Click to sort Ascending'
,
labelSortDesc:
type: String,
default: 'Click to sort Descending'
,
showEmpty:
type: Boolean,
default: false
,
emptyText:
type: String,
default: 'There are no records to show'
,
emptyFilteredText:
type: String,
default: 'There are no records matching your request'
,
watch:
items(newVal, oldVal)
console.log('items.watch');
if (oldVal === newVal)
return;
this.localItems = this.items;
,
sortDesc(newVal, oldVal)
console.log('watch sortDesc:', newVal, oldVal);
if (!this.noProviderSorting)
this.updater(newVal, oldVal);
,
sortBy(newVal, oldVal)
console.log('watch sortBy:', newVal, oldVal);
if (!this.noProviderSorting)
this.updater(newVal, oldVal);
,
perPage(newVal, oldVal)
console.log('watch perPage:', newVal, oldVal);
if (!this.noProviderPaging)
this.updater(newVal, oldVal);
,
currentPage(newVal, oldVal)
console.log('watch currentPage:', newVal, oldVal);
if (!this.noProviderPaging)
this.updater(newVal, oldVal);
,
filter(newVal, oldVal)
console.log('watch filter:', newVal, oldVal);
if (!this.noProviderFiltering)
this.updater(newVal, oldVal);
,
localItems(newVal, oldVal)
console.log('localItems updated');
,
computed:
tableClass()
return [
'table',
this.striped ? 'table-striped' : '',
this.hover ? 'table-hover' : '',
this.inverse ? 'table-inverse' : '',
this.bordered ? 'table-bordered' : '',
this.responsive ? 'table-responsive' : '',
this.small ? 'table-sm' : ''
];
,
_items()
if (!this.localItems)
if (this.itemsProvider)
this.updater(1,2);
return this.items || [];
else
this.localItems = this.items || [];
let items = this.localItems.slice();
// Apply local filter
if (this.filter && !(this.itemsProvider && !this.noProviderFiltering))
if (this.filter instanceof Function)
items = items.filter(this.filter);
else
let regex;
if (this.filter instanceof RegExp)
regex = this.filter;
else
regex = new RegExp('.*' + this.filter + '.*', 'ig');
items = items.filter(item =>
const test = regex.test(recToString(item));
regex.lastIndex = 0;
return test;
);
// Apply local Sort
const sortCompare = this.sortCompare || defaultSortCompare;
if (this.sortBy && !(this.itemsProvider && !this.noProviderSorting))
console.log('b-table sorting...');
items = items.sort((a, b) =>
const r = sortCompare(a, b, this.sortBy);
return this.sortDesc ? r : r * -1;
);
// Apply local pagination
if (this.perPage && !(this.itemsProvider && !this.noProviderPaging))
items = items.slice((this.currentPage - 1) * this.perPage, this.currentPage * this.perPage);
// Clear busy state
this.$nextTick(() =>
this.isBusy = false;
);
// Update the value model with the filtered/sorted/paginated data set
this.$emit('input', items);
return items;
,
methods:
fieldClass(field, key)
return [
field.sortable ? 'sorting' : '',
(field.sortable && this.sortBy === key) ? 'sorting_' + (this.sortDesc ? 'desc' : 'asc') : '',
field.variant ? ('table-' + field.variant) : '',
field.class ? field.class : ''
];
,
cellClass(field)
field.variant ? ('table-' + field.variant) : '',
field.class ? field.class : ''
,
rowClass(item)
// Prefer item._rowVariant over deprecated item.state
const variant = item._rowVariant || item.state || null;
return [
variant ? ('table-' + variant) : ''
];
,
rowClicked(e, item, index)
if (this.isBusy)
e.preventDefault();
e.stopPropagation();
return;
this.$emit('row-clicked', item, index);
,
headClick(e, field, key)
if (this.isBusy)
e.preventDefault();
e.stopPropagation();
return;
if (!field.sortable)
this.sortBy = null;
else
if (key === this.sortBy)
this.sortDesc = !this.sortDesc;
else
this.sortDesc = true;
this.sortBy = key;
this.$emit('head-clicked', key, this.sortDesc);
,
updater(a,b)
// @TODO: add providerDebounce
if (a === b || !this.itemsProvider || this.isBusy)
return;
// Set busy state
this.isBusy = true;
this.$nextTick(() =>
// If async, we just keep localItems as is, and awaite provider callback
const items = this.itemsProvider(this);
if (items)
this.localItems = items.slice();
);
,
providerCallback(data)
this.localItems = data ? data.slice() : [];
;
new Vue(
el: '#app',
components: bTable,
data:
fields:
name:
label: 'Person Full name',
sortable: true
,
age:
label: 'Person age',
sortable: true
,
isActive:
label: 'is Active'
,
actions:
label: 'Actions'
,
currentPage: 1,
perPage: 5,
filter: null,
async: true
,
methods:
details(item)
alert(JSON.stringify(item));
,
provider(ctx)
console.log('provider called', ctx);
let items = [
isActive: true,
age: 40,
name:
first: 'Dickerson',
last: 'Macdonald'
,
isActive: false,
age: 21,
name:
first: 'Larsen',
last: 'Shaw'
,
isActive: false,
age: 9,
state: 'success',
name:
first: 'Minni',
last: 'Navarro'
,
isActive: false,
age: 102,
name:
first: 'Woodrow',
last: 'Wilson'
,
isActive: true,
age: 38,
name:
first: 'Jami',
last: 'Carney'
,
isActive: false,
age: 42,
name:
first: 'Justin',
last: 'Truedeau'
,
isActive: true,
age: 72,
name:
first: 'Dickerson',
last: 'Macdonald Sr.'
,
isActive: false,
age: 12,
name:
first: 'Larsen',
last: 'Shaw Jr.'
,
isActive: false,
age: 26,
name:
first: 'Mitzi',
last: 'Navarro'
,
isActive: false,
age: 22,
name:
first: 'Geneva',
last: 'Wilson'
,
isActive: true,
age: 38,
name:
first: 'Janice',
last: 'Carney'
,
isActive: false,
age: 27,
name:
first: 'Essie',
last: 'Dunlap'
];
if (this.filter)
const regex = new RegExp('.*' + this.filter + '.*', 'ig');
items = items.filter(item =>
const test = regex.test(recToString(item));
//regex.lastIndex = 0;
return test;
);
if(ctx)
if (ctx.sortBy)
items = items.sort((a, b) =>
const r = defaultSortCompare(a, b, ctx.sortBy);
return ctx.sortDesc ? r : r * -1;
);
items = items.slice((this.currentPage - 1) * this.perPage, this.currentPage * this.perPage);
if (this.async)
// Emulate async request
const p = new Promise(resolve => setTimeout(resolve, 1000));
p.then(() =>
ctx.providerCallback(items);
);
else
// Non Async
return items;
else
// Our own app is requesting total # rows
return items;
);
#app
padding: 20px;
height: 500px;
html,body font-size: 14px;
table[aria-busy="false"]
opacity: 1;
table[aria-busy="true"]
opacity: .5;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="justify-content-center my-1 row">
<b-form-fieldset horizontal label-text-align="right" label="Provider:" class="col-4" :label-size="4">
<b-form-select class="form-control" :options="[text:'Async',value:true,text:'Sync',value:false]" v-model="async">
</b-form-select>
</b-form-fieldset>
<b-form-fieldset horizontal label-text-align="right" label="Page Size:" class="col-4" :label-size="6">
<b-form-select class="form-control" :options="[text:5,value:5,text:10,value:10,text:15,value:15]" v-model="perPage">
</b-form-select>
</b-form-fieldset>
<b-form-fieldset label-text-align="right" horizontal label="Filter:" class="col-4" :label-size="2">
<b-form-input v-model="filter" placeholder="Type to Search"></b-form-input>
</b-form-fieldset>
</div>
<!-- Main table element -->
<!-- :current-page="currentPage" :per-page="perPage" :filter="filter" no-provider-filtering -->
<b-table striped hover head-variant="inverse" :items-provider="provider" :fields="fields" :filter="filter" :current-page="currentPage" :per-page="perPage" show-empty>
<template slot="name" scope="item">
item.value.first item.value.last
</template>
<template slot="isActive" scope="item">
item.value?'Yes :)':'No :('
</template>
<template slot="actions" scope="item">
<b-btn size="sm" @click="details(item.item)">Details</b-btn>
</template>
</b-table>
<div class="justify-content-center row my-1">
<b-pagination size="md" :total-rows="provider(null).length" :per-page="perPage" v-model="currentPage" />
</div>
</div>
【问题讨论】:
【参考方案1】:在表格上使用 v-if 来检查过滤器是否有数据。 https://jsfiddle.net/Lsa5qkbt/
<b-table striped hover v-if="filter" head-variant="inverse" :items-provider="provider" :fields="fields" :filter="filter" :current-page="currentPage" :per-page="perPage" show-empty>
v-if 在满足/未满足条件时创建/销毁内容,因此在满足条件之前不使用任何资源。 https://vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
【讨论】:
以上是关于仅在应用过滤器时显示 bootstrap-vue b-table 中的项目的主要内容,如果未能解决你的问题,请参考以下文章