仅在应用过滤器时显示自举v-b表中的项目

问题描述 投票:0回答:1

是否有一种方法,当用户应用过滤器(在输入中输入值)时,仅在自举B表上显示项目?例如。如果“ filteredItems”不存在,什么也不显示?这主要是为了防止我的表呈现所有行(> 2k行)并妨碍性能。

带有b表的jsfiddle: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>
javascript vue.js bootstrap-vue
1个回答
2
投票

使用表格上的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

© www.soinside.com 2019 - 2024. All rights reserved.