From aed0d5eed2a8dc0defc1ace253cbec27dd30eac8 Mon Sep 17 00:00:00 2001 From: mertsincan Date: Mon, 17 May 2021 02:00:53 +0300 Subject: [PATCH] Fixed #508 - Edit and Sort combination support for Table --- api-generator/components/datatable.js | 11 +++++++ src/components/datatable/BodyCell.vue | 23 +++++++------ src/components/datatable/DataTable.d.ts | 1 + src/components/datatable/DataTable.vue | 44 ++++++++++++++++--------- src/components/datatable/TableBody.vue | 9 +++-- src/views/datatable/DataTableDoc.vue | 29 +++++++++------- 6 files changed, 77 insertions(+), 40 deletions(-) diff --git a/api-generator/components/datatable.js b/api-generator/components/datatable.js index 17dd33d24..9e38efb68 100644 --- a/api-generator/components/datatable.js +++ b/api-generator/components/datatable.js @@ -497,6 +497,17 @@ const DataTableEvents = [ } ] }, + { + name: "value-change", + description: "Invoked after filtering, sorting, pagination and cell editing to pass the rendered value.", + arguments: [ + { + name: "value", + type: "array", + description: "Value displayed by the table." + } + ] + }, { name: "row-click", description: "Callback to invoke when a row is clicked.", diff --git a/src/components/datatable/BodyCell.vue b/src/components/datatable/BodyCell.vue index 3997d1a76..20edb6631 100755 --- a/src/components/datatable/BodyCell.vue +++ b/src/components/datatable/BodyCell.vue @@ -39,7 +39,7 @@ import Ripple from 'primevue/ripple'; export default { name: 'BodyCell', - emits: ['cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', + emits: ['cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', 'editing-cell-change', 'row-toggle', 'radio-change', 'checkbox-change'], props: { rowData: { @@ -54,6 +54,10 @@ export default { type: Boolean, default: false }, + rowIndex: { + type: Number, + default: null + }, index: { type: Number, default: null @@ -153,6 +157,7 @@ export default { switchCellToViewMode() { this.d_editing = false; this.unbindDocumentEditListener(); + this.$emit('editing-cell-change', {rowIndex: this.rowIndex, cellIndex: this.index, editing: false}); OverlayEventBus.off('overlay-click', this.overlayEventListener); this.overlayEventListener = null; }, @@ -163,7 +168,8 @@ export default { if (!this.d_editing) { this.d_editing = true; this.bindDocumentEditListener(); - this.$emit('cell-edit-init', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.index}); + this.$emit('cell-edit-init', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.rowIndex}); + this.$emit('editing-cell-change', {rowIndex: this.rowIndex, cellIndex: this.index, editing: true}); this.overlayEventListener = (e) => { if (this.$el && this.$el.contains(e.target)) { @@ -179,7 +185,7 @@ export default { originalEvent: event, data: this.rowData, field: this.columnProp('field'), - index: this.index, + index: this.rowIndex, type: type, defaultPrevented: false, preventDefault: function() { @@ -202,7 +208,7 @@ export default { case 27: this.switchCellToViewMode(); - this.$emit('cell-edit-cancel', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.index}); + this.$emit('cell-edit-cancel', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.rowIndex}); break; case 9: @@ -291,13 +297,13 @@ export default { return (DomHandler.find(this.$el, '.p-invalid').length === 0); }, onRowEditInit(event) { - this.$emit('row-edit-init', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.index}); + this.$emit('row-edit-init', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.rowIndex}); }, onRowEditSave(event) { - this.$emit('row-edit-save', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.index}); + this.$emit('row-edit-save', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.rowIndex}); }, onRowEditCancel(event) { - this.$emit('row-edit-cancel', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.index}); + this.$emit('row-edit-cancel', {originalEvent: event, data: this.rowData, field: this.columnProp('field'), index: this.rowIndex}); }, updateStickyPosition() { if (this.columnProp('frozen')) { @@ -335,9 +341,6 @@ export default { let columnStyle = this.columnProp('style'); return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject]: [columnStyle, bodyStyle]; - }, - eventBusKey() { - return this.columnProp('field') + '_' + this.index; } }, components: { diff --git a/src/components/datatable/DataTable.d.ts b/src/components/datatable/DataTable.d.ts index dd9a7bb84..d6a556ef4 100755 --- a/src/components/datatable/DataTable.d.ts +++ b/src/components/datatable/DataTable.d.ts @@ -67,6 +67,7 @@ declare class DataTable { $emit(eventName: 'page', event: Event): this; $emit(eventName: 'sort', event: Event): this; $emit(eventName: 'filter', event: Event): this; + $emit(eventName: 'value-change', value: any[]): this; $emit(eventName: 'row-click', event: Event): this; $emit(eventName: 'row-dblclick', event: Event): this; $emit(eventName: 'row-contextmenu', event: Event): this; diff --git a/src/components/datatable/DataTable.vue b/src/components/datatable/DataTable.vue index 824197ae8..1729a037d 100755 --- a/src/components/datatable/DataTable.vue +++ b/src/components/datatable/DataTable.vue @@ -32,7 +32,7 @@ @row-mousedown="onRowMouseDown" @row-dragstart="onRowDragStart($event)" @row-dragover="onRowDragOver($event)" @row-dragleave="onRowDragLeave($event)" @row-dragend="onRowDragEnd($event)" @row-drop="onRowDrop($event)" @row-toggle="toggleRow($event)" @radio-change="toggleRowWithRadio($event)" @checkbox-change="toggleRowWithCheckbox($event)" @cell-edit-init="onCellEditInit($event)" @cell-edit-complete="onCellEditComplete($event)" @cell-edit-cancel="onCellEditCancel($event)" - @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)"/> + @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)" @editing-cell-change="onEditingCellChange($event)"/> + @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)" @editing-cell-change="onEditingCellChange($event)"/> @@ -73,11 +73,11 @@ import TableFooter from './TableFooter.vue'; export default { name: 'DataTable', - emits: ['update:first', 'update:rows', 'page', 'update:sortField', 'update:sortOrder', 'update:multiSortMeta', 'sort', 'filter', 'row-click', 'row-dblclick', + emits: ['value-change', 'update:first', 'update:rows', 'page', 'update:sortField', 'update:sortOrder', 'update:multiSortMeta', 'sort', 'filter', 'row-click', 'row-dblclick', 'update:selection', 'row-select', 'row-unselect', 'update:contextMenuSelection', 'row-contextmenu', 'row-unselect-all', 'row-select-all', 'column-resize-end', 'column-reorder', 'row-reorder', 'update:expandedRows', 'row-collapse', 'row-expand', 'update:expandedRowGroups', 'rowgroup-collapse', 'rowgroup-expand', 'update:filters', 'state-restore', 'state-save', - 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'update:editingRows', 'row-edit-init', 'row-edit-save', 'row-edit-cancel'], + 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'update:editingRows', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', 'editing-cell-change'], props: { value: { type: Array, @@ -323,7 +323,8 @@ export default { d_expandedRowKeys: null, d_columnOrder: null, d_editingRowKeys: null, - d_filters: this.cloneFilters(this.filters) + d_filters: this.cloneFilters(this.filters), + editingCells: [] }; }, rowTouched: false, @@ -425,6 +426,7 @@ export default { this.$emit('update:first', this.d_first); this.$emit('update:rows', this.d_rows); this.$emit('page', pageEvent); + this.$emit('value-change', this.processedData); }, onColumnHeaderClick(e) { const event = e.originalEvent; @@ -468,6 +470,7 @@ export default { } this.$emit('sort', this.createLazyLoadEvent(event)); + this.$emit('value-change', this.processedData); } } }, @@ -613,6 +616,7 @@ export default { let filterEvent = this.createLazyLoadEvent(); filterEvent.filteredValue = filteredValue; this.$emit('filter', filterEvent); + this.$emit('value-change', this.processedData); return filteredValue; }, @@ -1520,6 +1524,18 @@ export default { onCellEditCancel(event) { this.$emit('cell-edit-cancel', event); }, + onEditingCellChange(event) { + let { rowIndex, cellIndex, editing } = event; + let _editingCells = [...this.editingCells]; + + if (editing) + _editingCells.push({ rowIndex, cellIndex }); + else + _editingCells = _editingCells.filter(cell => !(cell.rowIndex === rowIndex && cell.cellIndex === cellIndex)); + + this.editingCells = _editingCells; + this.$emit('value-change', this.processedData); + }, onRowEditInit(event) { let _editingRows = this.editingRows ? [...this.editingRows] : []; _editingRows.push(event.data); @@ -1708,14 +1724,14 @@ export default { hasFilters() { return this.filters && Object.keys(this.filters).length > 0 && this.filters.constructor === Object; }, + hasEditingCell() { + return this.editingCells && this.editingCells.length !== 0; + }, processedData() { - if (this.lazy) { - return this.value || []; - } - else { - if (this.value && this.value.length) { - let data = this.value; + let data = this.value || []; + if (!this.lazy && !this.hasEditingCell) { + if (data && data.length) { if (this.sorted) { if(this.sortMode === 'single') data = this.sortSingle(data); @@ -1726,12 +1742,10 @@ export default { if (this.hasFilters) { data = this.filter(data); } - - return data; } - - return []; } + + return data; }, dataToRender() { const data = this.processedData; diff --git a/src/components/datatable/TableBody.vue b/src/components/datatable/TableBody.vue index 0c7c1d739..06ed06d94 100755 --- a/src/components/datatable/TableBody.vue +++ b/src/components/datatable/TableBody.vue @@ -15,13 +15,13 @@ @click="onRowClick($event, rowData, index)" @dblclick="onRowDblClick($event, rowData, index)" @contextmenu="onRowRightClick($event, rowData, index)" @touchend="onRowTouchEnd($event)" @keydown="onRowKeyDown($event, rowData, index)" :tabindex="selectionMode || contextMenu ? '0' : null" @mousedown="onRowMouseDown($event)" @dragstart="onRowDragStart($event, index)" @dragover="onRowDragOver($event,index)" @dragleave="onRowDragLeave($event)" @dragend="onRowDragEnd($event)" @drop="onRowDrop($event)" role="row"> @@ -52,7 +52,7 @@ export default { emits: ['rowgroup-toggle', 'row-click', 'row-dblclick', 'row-rightclick', 'row-touchend', 'row-keydown', 'row-mousedown', 'row-dragstart', 'row-dragover', 'row-dragleave', 'row-dragend', 'row-drop', 'row-toggle', 'radio-change', 'checkbox-change', 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', - 'row-edit-init', 'row-edit-save', 'row-edit-cancel'], + 'row-edit-init', 'row-edit-save', 'row-edit-cancel', 'editing-cell-change'], props: { value: { type: Array, @@ -447,6 +447,9 @@ export default { onRowEditCancel(event) { this.$emit('row-edit-cancel', event); }, + onEditingCellChange(event) { + this.$emit('editing-cell-change', event); + }, updateFrozenRowStickyPosition() { this.$el.style.top = DomHandler.getOuterHeight(this.$el.previousElementSibling) + 'px'; }, diff --git a/src/views/datatable/DataTableDoc.vue b/src/views/datatable/DataTableDoc.vue index f522cb599..66fe00cec 100755 --- a/src/views/datatable/DataTableDoc.vue +++ b/src/views/datatable/DataTableDoc.vue @@ -6,7 +6,7 @@ import DataTable from 'primevue/datatable'; import Column from 'primevue/column'; import ColumnGroup from 'primevue/columngroup'; //optional for column grouping - +
Getting Started
@@ -30,7 +30,7 @@ export default class CarService { return axios.get('demo/data/cars-large.json').then(res => res.data.data); } } - +

Example response;

@@ -49,7 +49,7 @@ export default class CarService { {"brand": "Fiat", "year": 2013, "color": "Red", "vin": "245t2s"} ] } - +

Following sample datatable has 4 columns and retrieves the data from a service on mount.

@@ -673,10 +673,10 @@ data() {
Filtering
-

DataTable has advanced filtering capabilities that does the heavy lifting while providing flexible customization. Filtering has two layout alternatives defined with the filterDisplay. +

DataTable has advanced filtering capabilities that does the heavy lifting while providing flexible customization. Filtering has two layout alternatives defined with the filterDisplay. In row setting, filter elements are displayed in a separate row at the header section whereas in menu mode filter elements are displayed inside an overlay. Filter metadata is specified using the filters as a v-model and UI elements for the filtering - are placed inside the filter template. The template filter gets a filterModel and filterCallback, + are placed inside the filter template. The template filter gets a filterModel and filterCallback, use filterModel.value to populate the filter with your own form components and call the filterCallback with the event of your choice like @input, @change, @click.


@@ -1735,16 +1735,16 @@ export default {
 
Responsive
-

DataTable responsive layout can be achieved in two ways; first approach is displaying a horizontal scrollbar for smaller screens +

DataTable responsive layout can be achieved in two ways; first approach is displaying a horizontal scrollbar for smaller screens and second one is defining a breakpoint to display the cells of a row as stacked. Scrollable tables use the scroll layout approach internally and do not require additional configuration.

Scroll Layout

Set responsiveLayout to scroll to enabled this layout. Note that, when scroll mode is enabled table-layout automatically switches to auto from fixed - as a result table widths are likely to differ and resizable columns are not supported. Read more about table-layout for more details.

+ as a result table widths are likely to differ and resizable columns are not supported. Read more about table-layout for more details.


 
@@ -1753,10 +1753,10 @@ export default {

In stack layout, columns are displayed as stacked after a certain breakpoint. Default is '960px'.


-
+
Row and Cell Styling

Certain rows or cells can easily be styled based on conditions. Cell styling is implemented with templating whereas row styling utilizes the rowClass property which takes the @@ -2234,6 +2234,11 @@ export default { Event to emit after filtering, not triggered in lazy mode. + + value-change + value: Value displayed by the table + Callback to invoke after filtering, sorting, pagination and cell editing to pass the rendered value. + row-click event.originalEvent: Browser event.
@@ -2704,7 +2709,7 @@ export default { }, mounted() { this.customerService.getCustomersLarge().then(data => { - this.customers = data; + this.customers = data; this.customers.forEach(customer => customer.date = new Date(customer.date)); this.loading = false; }); @@ -3034,4 +3039,4 @@ img { } } } - \ No newline at end of file +