diff --git a/src/components/column/Column.d.ts b/src/components/column/Column.d.ts index 37a2edfba..f6d377115 100644 --- a/src/components/column/Column.d.ts +++ b/src/components/column/Column.d.ts @@ -22,4 +22,5 @@ export declare class Column extends Vue { rowReorder?: boolean; rowReorderIcon?: string; reorderableColumn?: boolean; + rowEditor?: boolean; } \ No newline at end of file diff --git a/src/components/column/Column.vue b/src/components/column/Column.vue index d09655ac3..3e5f9f705 100644 --- a/src/components/column/Column.vue +++ b/src/components/column/Column.vue @@ -85,6 +85,10 @@ export default { reorderableColumn: { type: Boolean, default: true + }, + rowEditor: { + type: Boolean, + default: false } }, render() { diff --git a/src/components/datatable/BodyCell.vue b/src/components/datatable/BodyCell.vue index fe9068547..bf9e7fd3c 100644 --- a/src/components/datatable/BodyCell.vue +++ b/src/components/datatable/BodyCell.vue @@ -1,7 +1,7 @@ @@ -47,18 +58,31 @@ export default { type: Boolean, default: false }, + editing: { + type: Boolean, + default: false + }, + editMode: { + type: String, + default: null + } }, documentEditListener: null, data() { return { - editing: false + d_editing: this.editing + } + }, + watch: { + editing(newValue) { + this.d_editing = newValue; } }, mounted() { this.children = this.$children; }, updated() { - if (this.editing) { + if (this.d_editing) { let focusable = DomHandler.findSingle(this.$el, 'input'); if (focusable) { focusable.focus(); @@ -102,22 +126,22 @@ export default { } }, switchCellToViewMode() { - this.editing = false; + this.d_editing = false; this.unbindDocumentEditListener(); }, isOutsideClicked(event) { return !this.$el.contains(event.target) && !this.$el.isSameNode(event.target); }, onClick(event) { - if (this.isEditable() && !this.editing) { - this.editing = true; + if (this.editMode === 'cell' && this.isEditable() && !this.d_editing) { + this.d_editing = true; this.bindDocumentEditListener(); - this.$emit('edit-init', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); + this.$emit('cell-edit-init', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); } }, completeEdit(event, type) { let editEvent = {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index, type: type, preventDefault: () => event.preventDefault()}; - this.$emit('edit-complete', editEvent); + this.$emit('cell-edit-complete', editEvent); if (!event.defaultPrevented) { this.switchCellToViewMode(); @@ -131,7 +155,7 @@ export default { case 27: this.switchCellToViewMode(); - this.$emit('edit-cancel', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); + this.$emit('cell-edit-cancel', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); break; case 9: @@ -217,6 +241,15 @@ export default { }, isEditingCellValid() { return (DomHandler.find(this.$el, '.p-invalid').length === 0); + }, + onRowEditInit(event) { + this.$emit('row-edit-init', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); + }, + onRowEditSave(event) { + this.$emit('row-edit-save', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); + }, + onRowEditCancel(event) { + this.$emit('row-edit-cancel', {originalEvent: event, data: this.rowData, field: this.column.field, index: this.index}); } }, computed: { @@ -224,7 +257,7 @@ export default { return [this.column.bodyClass, { 'p-selection-column': this.column.selectionMode != null, 'p-editable-column': this.isEditable(), - 'p-cell-editing': this.editing + 'p-cell-editing': this.d_editing }]; } }, diff --git a/src/components/datatable/DataTable.d.ts b/src/components/datatable/DataTable.d.ts index 7a240cea4..b82ff065a 100644 --- a/src/components/datatable/DataTable.d.ts +++ b/src/components/datatable/DataTable.d.ts @@ -57,9 +57,12 @@ export declare class DataTable extends Vue { $emit(eventName: 'row-collapse', event: Event): this; $emit(eventName: 'rowgroup-expand', event: Event): this; $emit(eventName: 'rowgroup-collapse', event: Event): this; - $emit(eventName: 'edit-init', event: Event): this; - $emit(eventName: 'edit-complete', event: Event): this; - $emit(eventName: 'edit-cancel', event: Event): this; + $emit(eventName: 'cell-edit-init', event: Event): this; + $emit(eventName: 'cell-edit-complete', event: Event): this; + $emit(eventName: 'cell-edit-cancel', event: Event): this; + $emit(eventName: 'row-edit-init', event: Event): this; + $emit(eventName: 'row-edit-save', event: Event): this; + $emit(eventName: 'row-edit-cancel', event: Event): this; $slots: { header: VNode[]; paginatorLeft: VNode[]; diff --git a/src/components/datatable/DataTable.vue b/src/components/datatable/DataTable.vue index 7e1dd6983..f50fb703c 100644 --- a/src/components/datatable/DataTable.vue +++ b/src/components/datatable/DataTable.vue @@ -72,7 +72,9 @@ :rowTogglerIcon="col.expander ? rowTogglerIcon(rowData): null" @row-toggle="toggleRow" @radio-change="toggleRowWithRadio" @checkbox-change="toggleRowWithCheckbox" :rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(dataToRender, col, index) : null" - @edit-init="onEditInit" @edit-complete="onEditComplete" @edit-cancel="onEditCancel" /> + :editMode="editMode" :editing="editMode === 'row' && isRowEditing(rowData)" + @cell-edit-init="onCellEditInit" @cell-edit-complete="onCellEditComplete" @cell-edit-cancel="onCellEditCancel" + @row-edit-init="onRowEditInit" @row-edit-save="onRowEditSave" @row-edit-cancel="onRowEditCancel"/> @@ -308,6 +310,10 @@ export default { editMode: { type: String, default: null + }, + editingRows: { + type: Array, + default: null } }, data() { @@ -320,7 +326,8 @@ export default { d_multiSortMeta: this.multiSortMeta ? [...this.multiSortMeta] : [], d_selectionKeys: null, d_expandedRowKeys: null, - d_columnOrder: null + d_columnOrder: null, + d_editingRowKeys: null }; }, rowTouched: false, @@ -365,6 +372,11 @@ export default { if (this.dataKey) { this.updateExpandedRowKeys(newValue); } + }, + editingRows(newValue) { + if (this.dataKey) { + this.updateEditingRowKeys(newValue); + } } }, beforeMount() { @@ -854,6 +866,17 @@ export default { this.d_expandedRowKeys = null; } }, + updateEditingRowKeys(editingRows) { + if (editingRows && editingRows.length) { + this.d_editingRowKeys = {}; + for (let data of editingRows) { + this.d_editingRowKeys[String(ObjectUtils.resolveFieldData(data, this.dataKey))] = 1; + } + } + else { + this.d_editingRowKeys = null; + } + }, equals(data1, data2) { return this.compareSelectionBy === 'equals' ? (data1 === data2) : ObjectUtils.equals(data1, data2, this.dataKey); }, @@ -1563,14 +1586,42 @@ export default { headers.forEach((header, index) => header.style.width = widths[index] + 'px'); } }, - onEditInit(event) { - this.$emit('edit-init', event); + onCellEditInit(event) { + this.$emit('cell-edit-init', event); }, - onEditComplete(event) { - this.$emit('edit-complete', event); + onCellEditComplete(event) { + this.$emit('cell-edit-complete', event); }, - onEditCancel(event) { - this.$emit('edit-cancel', event); + onCellEditCancel(event) { + this.$emit('cell-edit-cancel', event); + }, + isRowEditing(rowData) { + if (rowData && this.editingRows) { + if (this.dataKey) + return this.d_editingRowKeys ? this.d_editingRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false; + else + return this.findIndex(rowData, this.editingRows) > -1; + } + + return false; + }, + onRowEditInit(event) { + let _editingRows = this.editingRows ? [...this.editingRows] : []; + _editingRows.push(event.data); + this.$emit('update:editingRows', _editingRows); + this.$emit('row-edit-init', event); + }, + onRowEditSave(event) { + let _editingRows = [...this.editingRows]; + _editingRows.splice(this.findIndex(event.data, this._editingRows), 1); + this.$emit('update:editingRows', _editingRows); + this.$emit('row-edit-save', event); + }, + onRowEditCancel(event) { + let _editingRows = [...this.editingRows]; + _editingRows.splice(this.findIndex(event.data, this._editingRows), 1); + this.$emit('update:editingRows', _editingRows); + this.$emit('row-edit-cancel', event); } }, computed: { diff --git a/src/views/datatable/DataTableEditDemo.vue b/src/views/datatable/DataTableEditDemo.vue index 24dcee76f..83b7db5a0 100644 --- a/src/views/datatable/DataTableEditDemo.vue +++ b/src/views/datatable/DataTableEditDemo.vue @@ -44,15 +44,15 @@

Advanced Cell Editing

Advanced editors with validations and ability to revert values with escape key.

- + @@ -69,10 +69,38 @@ + +

Row Editing

+ + + + + + + + + + + + +
@@ -80,161 +108,12 @@ -import CarService from '../../service/CarService'; -import DataTableSubMenu from './DataTableSubMenu'; -import Vue from 'vue'; -export default { - data() { - return { - cars1: null, - cars2: null, - editingCar: null, - editingCarIndex: null, - originalCar: null, - brands: [ - {brand: 'Audi', value: 'Audi'}, - {brand: 'BMW', value: 'BMW'}, - {brand: 'Fiat', value: 'Fiat'}, - {brand: 'Honda', value: 'Honda'}, - {brand: 'Jaguar', value: 'Jaguar'}, - {brand: 'Mercedes', value: 'Mercedes'}, - {brand: 'Renault', value: 'Renault'}, - {brand: 'Volkswagen', value: 'Volkswagen'}, - {brand: 'Volvo', value: 'Volvo'} - ] - } - }, - carService: null, - created() { - this.carService = new CarService(); - }, - methods: { - onEditInit(event) { - this.editingCarIndex = event.index; - this.editingCar = {...event.data}; //update on input - this.originalCar = {...event.data}; //revert with escape key - }, - onEditComplete(event) { - switch (event.field) { - case 'year': - if (this.isPositiveInteger(this.editingCar.year)) { - Vue.set(this.cars2, this.editingCarIndex, this.editingCar); - } - else { - this.$toast.add({severity:'error', summary: 'Validation Failed', detail:'Year must be a number', life: 3000}); - event.preventDefault(); - } - break; - - default: - if (this.editingCar[event.field].trim().length > 0) { - Vue.set(this.cars2, this.editingCarIndex, this.editingCar); - } - else { - this.$toast.add({severity:'error', summary: 'Validation Failed', detail: event.field + ' is required', life: 3000}); - event.preventDefault(); - } - break; - } - }, - clearEditorState() { - this.editingCar = null; - this.originalCar = null; - }, - onEdit(newValue, props) { - this.editingCar[props.column.field] = newValue; - }, - onEditCancel(event) { - Vue.set(this.cars2, event.index, this.originalCar); - this.editingCar = null; - }, - isPositiveInteger(val) { - let str = String(val); - str = str.trim(); - if (!str) { - return false; - } - str = str.replace(/^0+/, "") || "0"; - var n = Math.floor(Number(str)); - return n !== Infinity && String(n) === str && n >= 0; - } - }, - mounted() { - this.carService.getCarsSmall().then(data => this.cars1 = data); - this.carService.getCarsSmall().then(data => this.cars2 = data); - }, - components: { - 'DataTableSubMenu': DataTableSubMenu - } -} @@ -252,9 +131,11 @@ export default { return { cars1: null, cars2: null, + cars3: null, editingCar: null, editingCarIndex: null, originalCar: null, + editingRows: null, brands: [ {brand: 'Audi', value: 'Audi'}, {brand: 'BMW', value: 'BMW'}, @@ -273,12 +154,12 @@ export default { this.carService = new CarService(); }, methods: { - onEditInit(event) { + onCellEditInit(event) { this.editingCarIndex = event.index; this.editingCar = {...event.data}; //update on input this.originalCar = {...event.data}; //revert with escape key }, - onEditComplete(event) { + onCellEditComplete(event) { switch (event.field) { case 'year': if (this.isPositiveInteger(this.editingCar.year)) { @@ -301,17 +182,13 @@ export default { break; } }, - clearEditorState() { - this.editingCar = null; - this.originalCar = null; - }, - onEdit(newValue, props) { - this.editingCar[props.column.field] = newValue; - }, - onEditCancel(event) { + onCellEditCancel(event) { Vue.set(this.cars2, event.index, this.originalCar); this.editingCar = null; }, + onCellEdit(newValue, props) { + this.editingCar[props.column.field] = newValue; + }, isPositiveInteger(val) { let str = String(val); str = str.trim(); @@ -326,6 +203,7 @@ export default { mounted() { this.carService.getCarsSmall().then(data => this.cars1 = data); this.carService.getCarsSmall().then(data => this.cars2 = data); + this.carService.getCarsSmall().then(data => this.cars3 = data); }, components: { 'DataTableSubMenu': DataTableSubMenu