Initiated VirtualScroll Table

pull/104/head
cagataycivici 2019-11-19 14:54:24 +03:00
parent 2889aeae5b
commit 24b3ac7ae0
4 changed files with 205 additions and 34 deletions

View File

@ -61,7 +61,7 @@
@row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)"/>
</template>
<template #frozenbody>
<DTTableBody :value="frozenValue" :columns="frozenColumns" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode"
<DTTableBody v-if="frozenValue" :value="frozenValue" :columns="frozenColumns" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode"
:rowGroupMode="rowGroupMode" :groupRowsBy="groupRowsBy" :expandableRowGroups="expandableRowGroups" :rowClass="rowClass" :editMode="editMode" :compareSelectionBy="compareSelectionBy"
:expandedRowIcon="expandedRowIcon" :collapsedRowIcon="collapsedRowIcon" :expandedRows="expandedRows" :expandedRowKeys="d_expandedRowKeys" :expandedRowGroups="expandedRowGroups"
:editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$scopedSlots"
@ -75,7 +75,8 @@
<DTTableFooter :columnGroup="frozenFooterColumnGroup" :columns="frozenColumns" />
</template>
</DTScrollableView>
<DTScrollableView :scrollHeight="scrollHeight" :columns="scrollableColumns" :frozenWidth="frozenWidth">
<DTScrollableView :scrollHeight="scrollHeight" :columns="scrollableColumns" :frozenWidth="frozenWidth" :rows="rows"
:virtualScroll="virtualScroll" :virtualRowHeight="virtualRowHeight" :totalRecords="totalRecordsLength" @virtual-scroll="onVirtualScroll">
<template #header>
<DTTableHeader :columnGroup="headerColumnGroup" :columns="scrollableColumns" :rowGroupMode="rowGroupMode"
:groupRowsBy="groupRowsBy" :resizableColumns="resizableColumns" :allRowsSelected="allRowsSelected" :empty="empty"
@ -96,7 +97,7 @@
@row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)"/>
</template>
<template #frozenbody>
<DTTableBody :value="frozenValue" :columns="scrollableColumns" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode"
<DTTableBody v-if="frozenValue" :value="frozenValue" :columns="scrollableColumns" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode"
:rowGroupMode="rowGroupMode" :groupRowsBy="groupRowsBy" :expandableRowGroups="expandableRowGroups" :rowClass="rowClass" :editMode="editMode" :compareSelectionBy="compareSelectionBy"
:expandedRowIcon="expandedRowIcon" :collapsedRowIcon="collapsedRowIcon" :expandedRows="expandedRows" :expandedRowKeys="d_expandedRowKeys" :expandedRowGroups="expandedRowGroups"
:editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$scopedSlots"
@ -332,6 +333,18 @@ export default {
frozenWidth: {
type: String,
default: null
},
virtualScroll: {
type: Boolean,
default: false
},
virtualRowHeight: {
type: Number,
default: null
},
virtualScrollDelay: {
type: Number,
default: 159
}
},
data() {
@ -365,6 +378,7 @@ export default {
columnWidthsState: null,
tableWidthState: null,
columnWidthsRestored: false,
virtualScrollTimer: null,
watch: {
first(newValue) {
this.d_first = newValue;
@ -1488,6 +1502,18 @@ export default {
_editingRows.splice(this.findIndex(event.data, this._editingRows), 1);
this.$emit('update:editingRows', _editingRows);
this.$emit('row-edit-cancel', event);
},
onVirtualScroll(event) {
if(this.virtualScrollTimer) {
clearTimeout(this.virtualScrollTimer);
}
this.virtualScrollTimer = setTimeout(() => {
this.$emit('virtual-scroll', {
first: (event.page - 1) * this.rows,
rows: this.rows * 2
});
}, this.virtualScrollDelay);
}
},
computed: {
@ -1498,7 +1524,8 @@ export default {
'p-datatable-auto-layout': this.autoLayout,
'p-datatable-resizable': this.resizableColumns,
'p-datatable-resizable-fit': this.resizableColumns && this.columnResizeMode === 'fit',
'p-datatable-scrollable': this.scrollable
'p-datatable-scrollable': this.scrollable,
'p-datatable-virtual-scrollable': this.virtualScroll
}
];
},

View File

@ -12,12 +12,18 @@
</div>
</div>
<div class="p-datatable-scrollable-body" ref="scrollBody" @scroll="onBodyScroll">
<table ref="scrollTable">
<table ref="scrollTable" :class="bodyTableClass" :style="bodyTableStyle">
<colgroup class="p-datatable-scrollable-colgroup">
<col v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.headerStyle" />
</colgroup>
<slot name="body"></slot>
</table>
<table ref="loadingTable" :style="{top:'0', display: 'none'}" class="p-datatable-scrollable-body-table p-datatable-loading-virtual-table p-datatable-virtual-table">
<colgroup class="p-datatable-scrollable-colgroup">
<col v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.headerStyle" />
</colgroup>
</table>
<div class="p-datatable-virtual-scroller" ref="virtualScroller"></div>
</div>
<div class="p-datatable-scrollable-footer" ref="scrollFooter">
<div class="p-datatable-scrollable-footer-box" ref="scrollFooterBox">
@ -52,8 +58,25 @@ export default {
scrollHeight: {
type: String,
default: null
},
virtualScroll: {
type: Boolean,
default: false
},
virtualRowHeight: {
type: Number,
default: null
},
rows: {
type: Number,
default: null,
},
totalRecords: {
type: Number,
default: 0
}
},
virtualScrollCallback: null,
mounted() {
this.setScrollHeight();
@ -61,10 +84,29 @@ export default {
this.alignScrollBar();
else
this.$refs.scrollBody.style.paddingBottom = DomHandler.calculateScrollbarWidth() + 'px';
if (this.virtualScroll) {
this.$refs.virtualScroller.style.height = this.totalRecords * this.virtualRowHeight + 'px';
}
},
updated() {
if (!this.frozen) {
this.alignScrollBar();
}
if (this.virtualScrollCallback) {
this.virtualScrollCallback();
this.virtualScrollCallback = null;
}
},
watch: {
scrollHeight() {
this.setScrollHeight();
},
totalRecords(newValue) {
if (this.virtualScroll) {
this.$refs.virtualScroller.style.height = newValue * this.virtualRowHeight + 'px';
}
}
},
methods: {
@ -78,45 +120,43 @@ export default {
frozenScrollBody = DomHandler.findSingle(frozenView, '.p-datatable-scrollable-body');
}
if (frozenScrollBody) {
frozenScrollBody.scrollTop = this.$refs.scrollBody.scrollTop;
}
this.$refs.scrollHeaderBox.style.marginLeft = -1 * this.$refs.scrollBody.scrollLeft + 'px';
if (this.$refs.scrollFooterBox) {
this.$refs.scrollFooterBox.style.marginLeft = -1 * this.$refs.scrollBody.scrollLeft + 'px';
}
if (frozenScrollBody) {
frozenScrollBody.scrollTop = this.$refs.scrollBody.scrollTop;
}
/*
if (this.props.virtualScroll) {
let viewport = DomHandler.getClientHeight(this.scrollBody);
let tableHeight = DomHandler.getOuterHeight(this.scrollTable);
let pageHeight = this.props.virtualRowHeight * this.props.rows;
let virtualTableHeight = DomHandler.getOuterHeight(this.virtualScroller);
if (this.virtualScroll) {
let viewport = DomHandler.getClientHeight(this.$refs.scrollBody);
let tableHeight = DomHandler.getOuterHeight(this.$refs.scrollTable);
let pageHeight = this.virtualRowHeight * this.rows;
let virtualTableHeight = DomHandler.getOuterHeight(this.$refs.virtualScroller);
let pageCount = (virtualTableHeight / pageHeight)||1;
let scrollBodyTop = this.scrollTable.style.top||'0';
let scrollBodyTop = this.$refs.scrollTable.style.top||'0';
if(this.scrollBody.scrollTop + viewport > parseFloat(scrollBodyTop) + tableHeight || this.scrollBody.scrollTop < parseFloat(scrollBodyTop)) {
if (this.loadingTable) {
this.loadingTable.style.display = 'table';
this.loadingTable.style.top = this.scrollBody.scrollTop + 'px';
if(this.$refs.scrollBody.scrollTop + viewport > parseFloat(scrollBodyTop) + tableHeight || this.$refs.scrollBody.scrollTop < parseFloat(scrollBodyTop)) {
if (this.$refs.loadingTable) {
this.$refs.loadingTable.style.display = 'table';
this.$refs.loadingTable.style.top = this.$refs.scrollBody.scrollTop + 'px';
}
let page = Math.floor((this.scrollBody.scrollTop * pageCount) / (this.scrollBody.scrollHeight)) + 1;
if(this.props.onVirtualScroll) {
this.props.onVirtualScroll({
let page = Math.floor((this.$refs.scrollBody.scrollTop * pageCount) / (this.$refs.scrollBody.scrollHeight)) + 1;
this.$emit('virtual-scroll', {
page: page
});
this.virtualScrollCallback = () => {
if (this.loadingTable) {
this.loadingTable.style.display = 'none';
if (this.$refs.loadingTable) {
this.$refs.loadingTable.style.display = 'none';
}
this.scrollTable.style.top = ((page - 1) * pageHeight) + 'px';
this.$refs.scrollTable.style.top = ((page - 1) * pageHeight) + 'px';
}
}
}
}*/
},
setScrollHeight() {
if (this.scrollHeight) {
@ -184,6 +224,12 @@ export default {
else {
return null;
}
},
bodyTableClass() {
return ['p-datatable-scrollable-body-table', {'p-datatable-virtual-table': this.virtualScroll}];
},
bodyTableStyle() {
return this.virtualScroll ? {top: '0'} : null;
}
}
}

View File

@ -58,6 +58,21 @@ export default class DomHandler {
}
}
static getClientHeight(el, margin) {
if (el) {
let height = el.clientHeight;
if (margin) {
let style = getComputedStyle(el);
height += parseFloat(style.marginTop) + parseFloat(style.marginBottom);
}
return height;
} else {
return 0;
}
}
static getViewport() {
let win = window,
d = document,

View File

@ -18,6 +18,15 @@
<Column field="color" header="Color"></Column>
</DataTable>
<h3>Virtual Scroll</h3>
<DataTable :value="lazyCars" :scrollable="true" scrollHeight="200px" :lazy="true" :rows="20"
:virtualScroll="true" :virtualRowHeight="30" @virtual-scroll="onVirtualScroll" :totalRecords="lazyTotalRecords">
<Column field="vin" header="Vin"></Column>
<Column field="year" header="Year"></Column>
<Column field="brand" header="Brand"></Column>
<Column field="color" header="Color"></Column>
</DataTable>
<h3>Horizontal and Vertical</h3>
<DataTable :value="cars" :scrollable="true" scrollHeight="200px" style="width: 600px">
<Column field="vin" header="Vin" headerStyle="width: 250px" columnKey="vin_1"></Column>
@ -82,12 +91,58 @@ export default {
data() {
return {
cars: null,
frozenCars: null
frozenCars: null,
lazyCars: null,
lazyTotalRecords: 0
}
},
carService: null,
inmemoryData: null,
created() {
this.carService = new CarService();
this.inmemoryData = [
{"brand": "VW", "year": 2012, "color": "Orange"},
{"brand": "Audi", "year": 2011, "color": "Black"},
{"brand": "Renault", "year": 2005, "color": "Gray"},
{"brand": "BMW", "year": 2003, "color": "Blue"},
{"brand": "Mercedes", "year": 1995, "color": "Orange"},
{"brand": "Volvo", "year": 2005, "color": "Black"},
{"brand": "Honda", "year": 2012, "color": "Yellow"},
{"brand": "Jaguar", "year": 2013, "color": "Orange"},
{"brand": "Ford", "year": 2000, "color": "Black"},
{"brand": "Fiat", "year": 2013, "color": "Red"},
{"brand": "VW", "year": 2012, "color": "Orange"},
{"brand": "Audi", "year": 2011, "color": "Black"},
{"brand": "Renault", "year": 2005, "color": "Gray"},
{"brand": "BMW", "year": 2003, "color": "Blue"},
{"brand": "Mercedes", "year": 1995, "color": "Orange"},
{"brand": "Volvo", "year": 2005, "color": "Black"},
{"brand": "Honda", "year": 2012, "color": "Yellow"},
{"brand": "Jaguar", "year": 2013, "color": "Orange"},
{"brand": "Ford", "year": 2000, "color": "Black"},
{"brand": "Fiat", "year": 2013, "color": "Red"},
{"brand": "VW", "year": 2012, "color": "Orange"},
{"brand": "Audi", "year": 2011, "color": "Black"},
{"brand": "Renault", "year": 2005, "color": "Gray"},
{"brand": "BMW", "year": 2003, "color": "Blue"},
{"brand": "Mercedes", "year": 1995, "color": "Orange"},
{"brand": "Volvo", "year": 2005, "color": "Black"},
{"brand": "Honda", "year": 2012, "color": "Yellow"},
{"brand": "Jaguar", "year": 2013, "color": "Orange"},
{"brand": "Ford", "year": 2000, "color": "Black"},
{"brand": "Fiat", "year": 2013, "color": "Red"},
{"brand": "VW", "year": 2012, "color": "Orange"},
{"brand": "Audi", "year": 2011, "color": "Black"},
{"brand": "Renault", "year": 2005, "color": "Gray"},
{"brand": "BMW", "year": 2003, "color": "Blue"},
{"brand": "Mercedes", "year": 1995, "color": "Orange"},
{"brand": "Volvo", "year": 2005, "color": "Black"},
{"brand": "Honda", "year": 2012, "color": "Yellow"},
{"brand": "Jaguar", "year": 2013, "color": "Orange"},
{"brand": "Ford", "year": 2000, "color": "Black"},
{"brand": "Fiat", "year": 2013, "color": "Red"}
];
},
mounted() {
this.carService.getCarsLarge().then(data => this.cars = data);
@ -96,6 +151,34 @@ export default {
{brand: "BMW", year: 2013, color: "Grey", vin: "fh2uf23"},
{brand: "Chevrolet", year: 2011, color: "Black", vin: "4525g23"}
];
setTimeout(() => {
this.lazyCars = this.loadChunk(0, 40);
this.lazyTotalRecords = 250000;
}, 250);
},
methods: {
loadChunk(index, length) {
let chunk = [];
for (let i = 0; i < length; i++) {
chunk[i] = {...this.inmemoryData[i], ...{vin: (index + i)}};
}
return chunk;
},
onVirtualScroll(event) {
/*
For demo purposes keep loading the same dataset,
in a real production application, this data should come from server by building the query with LazyLoadEvent options
*/
setTimeout(() => {
//last chunk
if (event.first === 249980)
this.lazyCars = this.loadChunk(event.first, 20)
else
this.lazyCars = this.loadChunk(event.first, event.rows)
}, 250);
}
},
components: {
'DataTableSubMenu': DataTableSubMenu