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 @@
-
-
+
+
@@ -14,6 +14,17 @@
+
+
+
+
+
{{resolveFieldData()}}
|
@@ -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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{optionProps.option.brand}}
+
+
+
+
+
+
+
+
+
+
+
+
@@ -80,161 +108,12 @@
-<h3>Basic Cell Editing</h3>
-<p>Simple editors with v-model.</p>
-<DataTable :value="cars1" editMode="cell">
- <Column field="vin" header="Vin">
- <template #editor="slotProps">
- <InputText v-model="slotProps.data[slotProps.column.field]" />
- </template>
- </Column>
- <Column field="year" header="Year">
- <template #editor="slotProps">
- <InputText v-model="slotProps.data[slotProps.column.field]" />
- </template>
- </Column>
- <Column field="brand" header="Brand">
- <template #editor="slotProps">
- <Dropdown v-model="slotProps.data['brand']" :options="brands" optionLabel="brand" optionValue="value" placeholder="Select a Brand">
- <template #option="optionProps">
- <div class="p-dropdown-car-option">
- <img :alt="optionProps.option.brand" :src="'demo/images/car/' + optionProps.option.brand + '.png'" />
- <span>{{optionProps.option.brand}}</span>
- </div>
- </template>
- </Dropdown>
- </template>
- </Column>
- <Column field="color" header="Color">
- <template #editor="slotProps">
- <InputText v-model="slotProps.data[slotProps.column.field]" />
- </template>
- </Column>
-</DataTable>
-<h3>Advanced Cell Editing</h3>
-<p>Advanced editors with validations and ability to revert values with escape key.</p>
-<DataTable :value="cars2" editMode="cell" @edit-init="onEditInit" @edit-complete="onEditComplete" @edit-cancel="onEditCancel">
- <Column field="vin" header="Vin">
- <template #editor="slotProps">
- <InputText :value="slotProps.data[slotProps.column.field]" @input="onEdit($event, slotProps)" />
- </template>
- </Column>
- <Column field="year" header="Year">
- <template #editor="slotProps">
- <InputText :value="slotProps.data[slotProps.column.field]" @input="onEdit($event, slotProps)" />
- </template>
- </Column>
- <Column field="brand" header="Brand">
- <template #editor="slotProps">
- <Dropdown v-model="slotProps.data['brand']" :options="brands" optionLabel="brand" optionValue="value" placeholder="Select a Brand">
- <template #option="optionProps">
- <div class="p-dropdown-car-option">
- <img :alt="optionProps.option.brand" :src="'demo/images/car/' + optionProps.option.brand + '.png'" />
- <span>{{optionProps.option.brand}}</span>
- </div>
- </template>
- </Dropdown>
- </template>
- </Column>
- <Column field="color" header="Color">
- <template #editor="slotProps">
- <InputText :value="slotProps.data[slotProps.column.field]" @input="onEdit($event, slotProps)" />
- </template>
- </Column>
-</DataTable>
-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