Skip to content

Commit

Permalink
feat(igxTreeGrid): Row Editing (#2908)
Browse files Browse the repository at this point in the history
  • Loading branch information
ViktorSlavov authored and bazal4o committed Nov 5, 2018
1 parent adf3ce4 commit e9843b4
Show file tree
Hide file tree
Showing 33 changed files with 1,321 additions and 250 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -900,12 +900,18 @@
}

%igx-grid__td--edited {
font-style: italic;
color: --var($theme, 'cell-edited-value-color');
%grid-cell-text {
font-style: italic;
color: --var($theme, 'cell-edited-value-color');
}
}

%igx-grid__tr--deleted {
text-decoration-line: line-through;
%grid-cell-text {
font-style: italic;
color: igx-color(map-get($theme, 'palette'), 'error');
text-decoration-line: line-through;
}
}

%igx-grid__td--editing {
Expand Down
22 changes: 22 additions & 0 deletions projects/igniteui-angular/src/lib/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ export function cloneArray(array, deep?: boolean) {
return arr;
}

/**
* Doesn't clone leaf items
* @hidden
*/
export function cloneHierarchicalArray(array: any[], childDataKey: any): any[] {
const result: any[] = [];
if (!array) {
return result;
}

for (const item of array) {
if (Array.isArray(item[childDataKey])) {
const clonedItem = cloneValue(item);
clonedItem[childDataKey] = cloneHierarchicalArray(clonedItem[childDataKey], childDataKey);
result.push(clonedItem);
} else {
result.push(item);
}
}
return result;
}

/**
* Deep clones all first level keys of Obj2 and merges them to Obj1
* @param obj1 Object to merge into
Expand Down
66 changes: 48 additions & 18 deletions projects/igniteui-angular/src/lib/data-operations/data-util.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
import {
IgxFilteringOperand,
IgxBooleanFilteringOperand,
IgxDateFilteringOperand,
IgxNumberFilteringOperand,
IgxStringFilteringOperand
} from './filtering-condition';
import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface';
import { filteringStateDefaults, IFilteringState } from './filtering-state.interface';
import { FilteringStrategy, IFilteringStrategy } from './filtering-strategy';

import { ISortingExpression, SortingDirection } from './sorting-expression.interface';
import { ISortingState, SortingStateDefaults } from './sorting-state.interface';
import { ISortingStrategy, SortingStrategy, IGroupByResult, TreeGridSortingStrategy } from './sorting-strategy';

import { IGroupByResult, TreeGridSortingStrategy } from './sorting-strategy';
import { IPagingState, PagingError } from './paging-state.interface';

import { IDataState } from './data-state.interface';
import { IGroupByExpandState, IGroupByKey } from './groupby-expand-state.interface';
import { IGroupByRecord } from './groupby-record.interface';
import { IGroupingState } from './groupby-state.interface';
import { Transaction, TransactionType } from '../services';
import { Transaction, TransactionType, HierarchicalTransaction, IgxHierarchicalTransactionService, HierarchicalState } from '../services';
import { mergeObjects, cloneValue } from '../core/utils';
import { ITreeGridRecord } from '../grids/tree-grid/tree-grid.interfaces';

export enum DataType {
Expand Down Expand Up @@ -79,7 +67,8 @@ export class DataUtil {
children: hierarchicalRecord.children,
isFilteredOutParent: hierarchicalRecord.isFilteredOutParent,
level: hierarchicalRecord.level,
expanded: hierarchicalRecord.expanded
expanded: hierarchicalRecord.expanded,
path: [...hierarchicalRecord.path]
};
return rec;
}
Expand Down Expand Up @@ -224,9 +213,12 @@ export class DataUtil {
* @param primaryKey Primary key of the collection, if any
*/
public static mergeTransactions<T>(data: T[], transactions: Transaction[], primaryKey?: any): T[] {
data.forEach((value, index) => {
const rowId = primaryKey ? value[primaryKey] : value;
data.forEach((item: any, index: number) => {
const rowId = primaryKey ? item[primaryKey] : item;
const transaction = transactions.find(t => t.id === rowId);
if (Array.isArray(item.children)) {
this.mergeTransactions(item.children, transactions, primaryKey);
}
if (transaction && transaction.type === TransactionType.UPDATE) {
data[index] = transaction.newValue;
}
Expand All @@ -237,4 +229,42 @@ export class DataUtil {
.map(t => t.newValue));
return data;
}

// TODO: optimize addition of added rows. Should not filter transaction in each recursion!!!
/** @experimental @hidden */
public static mergeHierarchicalTransactions(
data: any[],
transactions: HierarchicalTransaction[],
childDataKey: any,
primaryKey?: any,
parentKey?: any): any[] {

for (let index = 0; index < data.length; index++) {
const dataItem = data[index];
const rowId = primaryKey ? dataItem[primaryKey] : dataItem;
const updateTransaction = transactions.filter(t => t.type === TransactionType.UPDATE).find(t => t.id === rowId);
const addedTransactions = transactions.filter(t => t.type === TransactionType.ADD).filter(t => t.parentId === rowId);
if (updateTransaction || addedTransactions.length > 0) {
data[index] = mergeObjects(cloneValue(dataItem), updateTransaction && updateTransaction.newValue);
}
if (addedTransactions.length > 0) {
if (!data[index][childDataKey]) {
data[index][childDataKey] = [];
}
for (const addedTransaction of addedTransactions) {
data[index][childDataKey].push(addedTransaction.newValue);
}
}
if (data[index][childDataKey]) {
data[index][childDataKey] = this.mergeHierarchicalTransactions(
data[index][childDataKey],
transactions,
childDataKey,
primaryKey,
rowId
);
}
}
return data;
}
}
8 changes: 6 additions & 2 deletions projects/igniteui-angular/src/lib/grids/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
dataWithTransactions.map((record) => record[grid.primaryKey]).indexOf(rowID) :
dataWithTransactions.indexOf(rowID);
if (rowIndex !== -1) {
// Check if below change will work on added rows with transactions
// oldValue = this.get_all_data(id, true)[rowIndex][column.field];
// rowData = this.get_all_data(id, true)[rowIndex];
oldValue = columnID !== null ? dataWithTransactions[rowIndex][column.field] : null;
rowData = dataWithTransactions[rowIndex];
}
Expand Down Expand Up @@ -596,9 +599,10 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
return column.dataType === DataType.Number;
}

public get_all_data(id: string): any[] {
public get_all_data(id: string, transactions?: boolean): any[] {
const grid = this.get(id);
return grid.data;
const data = transactions ? grid.dataWithAddedInTransactionRows : grid.data;
return data ? data : [];
}

protected getSortStrategyPerColumn(id: string, fieldName: string) {
Expand Down
81 changes: 48 additions & 33 deletions projects/igniteui-angular/src/lib/grids/grid-base.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
import { Subject } from 'rxjs';
import { takeUntil, first } from 'rxjs/operators';
import { IgxSelectionAPIService } from '../core/selection';
import { cloneArray, isNavigationKey, CancelableEventArgs } from '../core/utils';
import { cloneArray, isNavigationKey, mergeObjects, CancelableEventArgs } from '../core/utils';
import { DataType, DataUtil } from '../data-operations/data-util';
import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface';
import { IGroupByExpandState } from '../data-operations/groupby-expand-state.interface';
Expand Down Expand Up @@ -1633,7 +1633,7 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
/**
* Get transactions service for the grid.
*/
get transactions() {
get transactions(): TransactionService<Transaction, State> {
return this._transactions;
}

Expand Down Expand Up @@ -2062,7 +2062,9 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
this.zone.run(() => {
this.cdr.detectChanges();
this.verticalScrollContainer.onChunkLoad.emit(this.verticalScrollContainer.state);
this.changeRowEditingOverlayStateOnScroll(this.rowInEditMode);
if (this.rowEditable) {
this.changeRowEditingOverlayStateOnScroll(this.rowInEditMode);
}
});
}

Expand Down Expand Up @@ -2097,7 +2099,7 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
constructor(
private gridAPI: GridBaseAPIService<IgxGridBaseComponent>,
public selection: IgxSelectionAPIService,
@Inject(IgxGridTransaction) private _transactions: TransactionService,
@Inject(IgxGridTransaction) protected _transactions: TransactionService<Transaction, State>,
private elementRef: ElementRef,
private zone: NgZone,
@Inject(DOCUMENT) public document,
Expand Down Expand Up @@ -2859,26 +2861,17 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
return;
}

// TODO: should we emit this when cascadeOnDelete is true for each row?!?!
this.onRowDeleted.emit({ data: data[index] });

// if there is a row (index !== 0) delete it
// if there is a row in ADD or UPDATE state change it's state to DELETE
if (index !== -1) {
if (this.transactions.enabled) {
const transaction: Transaction = { id: rowId, type: TransactionType.DELETE, newValue: null };
this.transactions.add(transaction, data[index]);
} else {
this.deleteRowFromData(rowId, index);
}
} else {
this.transactions.add({ id: rowId, type: TransactionType.DELETE, newValue: null }, state.recordRef);
}

if (this.rowSelectable === true && this.selection.is_item_selected(this.id, rowId)) {
// first deselect row then delete it
if (this.rowSelectable && this.selection.is_item_selected(this.id, rowId)) {
this.deselectRows([rowId]);
} else {
this.checkHeaderCheckboxStatus();
}

this.deleteRowFromData(rowId, index);
this._pipeTrigger++;
this.cdr.markForCheck();

Expand All @@ -2892,7 +2885,19 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
* @hidden
*/
protected deleteRowFromData(rowID: any, index: number) {
this.data.splice(index, 1);
// if there is a row (index !== 0) delete it
// if there is a row in ADD or UPDATE state change it's state to DELETE
if (index !== -1) {
if (this.transactions.enabled) {
const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
this.transactions.add(transaction, this.data[index]);
} else {
this.data.splice(index, 1);
}
} else {
const state: State = this.transactions.getState(rowID);
this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
}
}

/**
Expand Down Expand Up @@ -3542,7 +3547,7 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
if (this.rowSelectable) {
this.calcRowCheckboxWidth = this.headerCheckboxContainer.nativeElement.clientWidth;
}
if (this.rowEditable && !this.rowEditingOverlay.collapsed) {
if (this.rowEditable) {
this.repositionRowEditingOverlay(this.rowInEditMode);
}
this.cdr.detectChanges();
Expand Down Expand Up @@ -3842,7 +3847,7 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
* this.grid.selectRows([1,2,5], true);
* ```
* @param rowIDs
* @param clearCurrentSelection if true clears the curren selection
* @param clearCurrentSelection if true clears the current selection
* @memberof IgxGridBaseComponent
*/
public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
Expand Down Expand Up @@ -4319,18 +4324,18 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
this.nativeElement.focus();
} */

private changeRowEditingOverlayStateOnScroll(row: IgxRowComponent<IgxGridBaseComponent>) {
if (!this.rowEditable || this.rowEditingOverlay.collapsed) {
return;
}
if (!row) {
this.toggleRowEditingOverlay(false);
} else {
this.repositionRowEditingOverlay(row);
private changeRowEditingOverlayStateOnScroll(row: IgxRowComponent<IgxGridBaseComponent>) {
if (!this.rowEditable || this.rowEditingOverlay.collapsed) {
return;
}
if (!row) {
this.toggleRowEditingOverlay(false);
} else {
this.repositionRowEditingOverlay(row);
}
}
}

/**
/**
* @hidden
*/
public startRowEdit(cell: {
Expand Down Expand Up @@ -4365,6 +4370,7 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
this.rowEditPositioningStrategy.isTopInitialPosition = null;
this.rowEditingOverlay.close();
this.rowEditingOverlay.element.parentElement.style.display = '';
}

/**
Expand All @@ -4383,9 +4389,15 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
* @hidden
*/
public repositionRowEditingOverlay(row: IgxRowComponent<IgxGridBaseComponent>) {
this.configureRowEditingOverlay(row.rowID);
if (!this.rowEditingOverlay.collapsed) {
this.rowEditingOverlay.reposition();
const rowStyle = this.rowEditingOverlay.element.parentElement.style;
if (row) {
rowStyle.display = '';
this.configureRowEditingOverlay(row.rowID);
this.rowEditingOverlay.reposition();
} else {
rowStyle.display = 'none';
}
}
}

Expand All @@ -4412,6 +4424,9 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
return rowChanges ? Object.keys(rowChanges).length : 0;
}

protected writeToData(rowIndex: number, value: any) {
mergeObjects(this.data[rowIndex], value);
}
/**
* TODO: Refactor
* @hidden
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,7 @@ export class IgxGridAPIService extends GridBaseAPIService<IgxGridComponent> {
}
this.get(id).groupingExpansionState = expansionState;
if (grid.rowEditable) {
if (toggleRowEditingOverlay !== undefined) {
grid.toggleRowEditingOverlay(toggleRowEditingOverlay);
}

// If row overlay is opened in a group and another group is expanded/collapsed,
// then the row in edit will move down/up and therefore the row edit overlay should move down/up.
if (grid.rowInEditMode && !grid.rowEditingOverlay.collapsed) {
grid.repositionRowEditingOverlay(grid.rowInEditMode);
}
grid.repositionRowEditingOverlay(grid.rowInEditMode);
}
}

Expand Down
Loading

0 comments on commit e9843b4

Please sign in to comment.