Skip to content

Commit 5399bb4

Browse files
authored
Merge branch 'master' into iganchev/add-i18n-row-edit-btns-master
2 parents 66656a4 + ed6682a commit 5399bb4

File tree

44 files changed

+819
-345
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+819
-345
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ All notable changes for each version of this project will be documented in this
3030
</ng-template>
3131
</igx-column>
3232
```
33+
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
34+
- Added `batchEditing` - an `Input` property for controlling what type of transaction service is provided for the grid.
35+
Setting `<igx-grid [batchEditing]="true">` is the same as providing `[{ provide: IgxGridTransaction, useClass: IgxTransactionService }]`.
36+
- **Deprecation** - Providing a transaction service for the grid via `providers: [IgxTransactionService]` is now deprecated and will be removed in a future patch.
37+
Instead, use the new `batchEditing` property to control the grid's Transactions.
38+
39+
```html
40+
<igx-grid #grid [data]="data" [batchEditing]="true">
41+
...
42+
</igx-grid>
43+
<button igxButton (click)="grid.transactions.undo">Undo</button>
44+
```
45+
46+
- `Transactions`
47+
- Added `IgxFlatTransactionFactory` - the singleton service instantiates a new `TransactionService<Transaction, State>` given a `transaction type`.
48+
- Added `IgxHierarchicalTransactionFactory` - the singleton service instantiates a new `HierarchicalTransactionService<HierarchicalTransaction, HierarchicalState>` given a `transaction type`.
49+
3350
- `Toolbar Actions`
3451
- Exposed a new input property `overlaySettings` for all column actions (`hiding` | `pinning` | `advanced filtering` | `exporter`). Example below:
3552

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -673,9 +673,9 @@
673673
$hierarchical-action-icon: 24px;
674674

675675
$grouparea-padding: (
676-
comfortable: rem(8px) rem(24px),
677-
cosy: rem(8px) rem(16px),
678-
compact: rem(4px) rem(12px)
676+
comfortable: 0 rem(24px),
677+
cosy: 0 rem(16px),
678+
compact: 0 rem(12px)
679679
);
680680

681681
$grouparea-min-height: (
@@ -2155,6 +2155,11 @@
21552155
&:focus {
21562156
outline-style: none;
21572157
}
2158+
2159+
%igx-chip {
2160+
margin-top: rem(8px);
2161+
margin-bottom: rem(8px);
2162+
}
21582163
}
21592164

21602165
%igx-grid-grouparea__connector {
@@ -2177,11 +2182,21 @@
21772182
%igx-grid-grouparea--cosy {
21782183
min-height: map-get($grouparea-min-height, 'cosy');
21792184
padding: map-get($grouparea-padding, 'cosy');
2185+
2186+
%igx-chip {
2187+
margin-top: rem(8px);
2188+
margin-bottom: rem(8px);
2189+
}
21802190
}
21812191

21822192
%igx-grid-grouparea--compact {
21832193
min-height: map-get($grouparea-min-height, 'compact');
21842194
padding: map-get($grouparea-padding, 'compact');
2195+
2196+
%igx-chip {
2197+
margin-top: rem(4px);
2198+
margin-bottom: rem(4px);
2199+
}
21852200
}
21862201

21872202
%igx-drop-area {

projects/igniteui-angular/src/lib/grids/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ Below is the list of all inputs that the developers may set to configure the gri
166166
|`data`|Array|The data source for the grid.|
167167
|`resourceStrings`| IGridResourceStrings | Resource strings of the grid. |
168168
|`autoGenerate`|boolean|Autogenerate grid's columns, default value is _false_|
169+
|`batchEditing`|boolean|Toggles batch editing in the grid, default is _false_|
169170
|`paging`|boolean|Enables the paging feature. Defaults to _false_.|
170171
|`page`| number | The current page index.|
171172
|`perPage`|number|Visible items per page, default is 15|

projects/igniteui-angular/src/lib/grids/common/events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export interface IColumnMovingEventArgs extends IBaseEventArgs {
125125
export interface IColumnMovingEndEventArgs extends IBaseEventArgs {
126126
source: IgxColumnComponent;
127127
target: IgxColumnComponent;
128+
cancel: boolean;
128129
}
129130

130131
export interface IGridKeydownEventArgs extends IBaseEventArgs {

projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export class IgxGridExcelStyleFilteringComponent implements OnDestroy {
207207
this.cdr.detectChanges();
208208
});
209209
this._columnMoved = this.grid.columnMovingEnd.pipe(takeUntil(this.destroy$)).subscribe(() => {
210-
this.cdr.detectChanges();
210+
this.cdr.markForCheck();
211211
});
212212
}
213213
}

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
} from '@angular/core';
3232
import { resizeObservable } from '../core/utils';
3333
import 'igniteui-trial-watermark';
34-
import { Subject, pipe, fromEvent, animationFrameScheduler } from 'rxjs';
34+
import { Subject, pipe, fromEvent, animationFrameScheduler, merge } from 'rxjs';
3535
import { takeUntil, first, filter, throttleTime, map, shareReplay, takeWhile } from 'rxjs/operators';
3636
import { cloneArray, mergeObjects, compareMaps, resolveNestedPath, isObject, PlatformUtil } from '../core/utils';
3737
import { GridColumnDataType } from '../data-operations/data-util';
@@ -50,7 +50,8 @@ import {
5050
ConnectedPositioningStrategy,
5151
ContainerPositionStrategy,
5252
StateUpdateEvent,
53-
TransactionEventOrigin
53+
TransactionEventOrigin,
54+
Action,
5455
} from '../services/public_api';
5556
import { GridBaseAPIService } from './api.service';
5657
import { IgxGridCellComponent } from './cell.component';
@@ -154,6 +155,7 @@ import { IPageEventArgs } from '../paginator/paginator-interfaces';
154155
import { IgxPaginatorComponent } from '../paginator/paginator.component';
155156
import { IgxGridHeaderRowComponent } from './headers/grid-header-row.component';
156157
import { IgxGridGroupByAreaComponent } from './grouping/grid-group-by-area.component';
158+
import { IgxFlatTransactionFactory, TRANSACTION_TYPE } from '../services/transaction/transaction-factory.service';
157159

158160
let FAKE_ROW_ID = -1;
159161
const DEFAULT_ITEMS_PER_PAGE = 15;
@@ -171,6 +173,7 @@ export const IgxGridTransaction = new InjectionToken<string>('IgxGridTransaction
171173
@Directive()
172174
export abstract class IgxGridBaseDirective extends DisplayDensityBase implements GridType,
173175
OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
176+
174177
/**
175178
* Gets/Sets the display time for the row adding snackbar notification.
176179
*
@@ -2365,6 +2368,31 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
23652368
this.notifyChanges();
23662369
}
23672370

2371+
/**
2372+
* Gets/Sets whether the grid has batch editing enabled.
2373+
* When batch editing is enabled, changes are not made directly to the underlying data.
2374+
* Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2375+
*
2376+
* @example
2377+
* ```html
2378+
* <igx-grid [batchEditing]="true" [data]="someData">
2379+
* </igx-grid>
2380+
* ```
2381+
*/
2382+
@Input()
2383+
public get batchEditing(): boolean {
2384+
return this._batchEditing;
2385+
}
2386+
2387+
public set batchEditing(val: boolean) {
2388+
if (val !== this._batchEditing) {
2389+
delete this._transactions;
2390+
this._batchEditing = val;
2391+
this.switchTransactionService(val);
2392+
this.subscribeToTransactions();
2393+
}
2394+
}
2395+
23682396
/** @hidden @internal */
23692397
public get pinnedColumnsTextInternal() {
23702398
return this._pinnedColumnsText;
@@ -2374,6 +2402,9 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
23742402
* Get transactions service for the grid.
23752403
*/
23762404
public get transactions(): TransactionService<Transaction, State> {
2405+
if (this._diTransactions && !this.batchEditing) {
2406+
return this._diTransactions;
2407+
}
23772408
return this._transactions;
23782409
}
23792410

@@ -2825,6 +2856,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
28252856
protected _init = true;
28262857
protected _cdrRequestRepaint = false;
28272858
protected _userOutletDirective: IgxOverlayOutletDirective;
2859+
protected _transactions: TransactionService<Transaction, State>;
2860+
protected _batchEditing = false;
28282861

28292862
/** @hidden @internal */
28302863
public get paginator() {
@@ -2928,6 +2961,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
29282961
positionStrategy: this.rowEditPositioningStrategy
29292962
};
29302963

2964+
private transactionChange$ = new Subject<void>();
2965+
29312966
private readonly DRAG_SCROLL_DELTA = 10;
29322967

29332968
/**
@@ -3056,7 +3091,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
30563091
public selectionService: IgxGridSelectionService,
30573092
public colResizingService: IgxColumnResizingService,
30583093
public gridAPI: GridBaseAPIService<IgxGridBaseDirective & GridType>,
3059-
@Inject(IgxGridTransaction) protected _transactions: TransactionService<Transaction, State>,
3094+
protected transactionFactory: IgxFlatTransactionFactory,
30603095
private elementRef: ElementRef<HTMLElement>,
30613096
private zone: NgZone,
30623097
@Inject(DOCUMENT) public document: any,
@@ -3070,13 +3105,15 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
30703105
public summaryService: IgxGridSummaryService,
30713106
@Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions,
30723107
@Inject(LOCALE_ID) private localeId: string,
3073-
protected platform: PlatformUtil) {
3108+
protected platform: PlatformUtil,
3109+
@Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>) {
30743110
super(_displayDensityOptions);
30753111
this.locale = this.locale || this.localeId;
30763112
this.datePipe = new DatePipe(this.locale);
30773113
this.decimalPipe = new DecimalPipe(this.locale);
30783114
this.currencyPipe = new CurrencyPipe(this.locale);
30793115
this.percentPipe = new PercentPipe(this.locale);
3116+
this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
30803117
this.cdr.detach();
30813118
}
30823119

@@ -3289,25 +3326,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
32893326
this.summaryService.clearSummaryCache(args);
32903327
});
32913328

3292-
this.transactions.onStateUpdate.pipe(destructor).subscribe((event: StateUpdateEvent) => {
3293-
let actions = [];
3294-
if (event.origin === TransactionEventOrigin.REDO) {
3295-
actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
3296-
} else if (event.origin === TransactionEventOrigin.UNDO) {
3297-
actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
3298-
}
3299-
if (actions.length > 0) {
3300-
for (const action of actions) {
3301-
if (this.selectionService.isRowSelected(action.transaction.id)) {
3302-
this.selectionService.deselectRow(action.transaction.id);
3303-
}
3304-
}
3305-
}
3306-
this.selectionService.clearHeaderCBState();
3307-
this.summaryService.clearSummaryCache();
3308-
this.pipeTrigger++;
3309-
this.notifyChanges();
3310-
});
3329+
this.subscribeToTransactions();
33113330

33123331
this.resizeNotify.pipe(
33133332
destructor,
@@ -3688,6 +3707,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
36883707

36893708
this.destroy$.next(true);
36903709
this.destroy$.complete();
3710+
this.transactionChange$.next();
3711+
this.transactionChange$.complete();
36913712
this._destroyed = true;
36923713

36933714
if (this._advancedFilteringOverlayId) {
@@ -4283,6 +4304,14 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
42834304
* ```
42844305
*/
42854306
public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
4307+
// M.A. May 11th, 2021 #9508 Make the event cancelable
4308+
const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
4309+
4310+
this.columnMovingEnd.emit(eventArgs);
4311+
4312+
if (eventArgs.cancel) {
4313+
return;
4314+
}
42864315

42874316
if (column === target || (column.level !== target.level) ||
42884317
(column.topLevelParent !== target.topLevelParent)) {
@@ -4318,8 +4347,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
43184347

43194348
this._moveColumns(column, target, pos);
43204349
this._columnsReordered(column);
4321-
4322-
this.columnMovingEnd.emit({ source: column, target });
43234350
}
43244351

43254352
/**
@@ -5996,6 +6023,40 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
59966023
this.crudService.endEdit(commit, event);
59976024
}
59986025

6026+
protected switchTransactionService(val: boolean) {
6027+
if (val) {
6028+
this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
6029+
} else {
6030+
this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
6031+
}
6032+
}
6033+
6034+
protected subscribeToTransactions(): void {
6035+
this.transactionChange$.next();
6036+
this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$,this.transactionChange$)))
6037+
.subscribe(this.transactionStatusUpdate.bind(this));
6038+
}
6039+
6040+
protected transactionStatusUpdate(event: StateUpdateEvent) {
6041+
let actions: Action<Transaction>[] = [];
6042+
if (event.origin === TransactionEventOrigin.REDO) {
6043+
actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
6044+
} else if (event.origin === TransactionEventOrigin.UNDO) {
6045+
actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
6046+
}
6047+
if (actions.length > 0) {
6048+
for (const action of actions) {
6049+
if (this.selectionService.isRowSelected(action.transaction.id)) {
6050+
this.selectionService.deselectRow(action.transaction.id);
6051+
}
6052+
}
6053+
}
6054+
this.selectionService.clearHeaderCBState();
6055+
this.summaryService.clearSummaryCache();
6056+
this.pipeTrigger++;
6057+
this.notifyChanges();
6058+
};
6059+
59996060
protected writeToData(rowIndex: number, value: any) {
60006061
mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
60016062
}

projects/igniteui-angular/src/lib/grids/grid-common.module.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import { IgxGridFooterComponent } from './grid-footer/grid-footer.component';
44
import {
55
IgxGridBodyDirective
66
} from './grid.common';
7-
import { IgxGridTransaction } from './grid-base.directive';
8-
import { IgxBaseTransactionService } from '../services/transaction/base-transaction';
97
import {
108
IgxRowAddTextDirective,
119
IgxRowEditTemplateDirective,
@@ -39,6 +37,7 @@ import {
3937
} from './grid/grid.directives';
4038
import { IgxChipsModule } from '../chips/chips.module';
4139
import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive';
40+
4241
/**
4342
* @hidden
4443
*/
@@ -113,9 +112,6 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive';
113112
IgxPaginatorModule,
114113
IgxGridSharedModules,
115114
IgxChipsModule
116-
],
117-
providers: [
118-
{ provide: IgxGridTransaction, useClass: IgxBaseTransactionService }
119115
]
120116
})
121117
export class IgxGridCommonModule { }

projects/igniteui-angular/src/lib/grids/grid/column-moving.spec.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ describe('IgxGrid - Column Moving #grid', () => {
124124
column.move(2);
125125

126126
columnsList = grid.columnList.toArray();
127-
const args = { source: grid.columnList.toArray()[2], target: grid.columnList.toArray()[1] };
127+
const args = { source: grid.columnList.toArray()[2], target: grid.columnList.toArray()[1], cancel: false };
128128
expect(grid.columnMovingEnd.emit).toHaveBeenCalledTimes(1);
129129
expect(grid.columnMovingEnd.emit).toHaveBeenCalledWith(args);
130130
});
@@ -456,6 +456,7 @@ describe('IgxGrid - Column Moving #grid', () => {
456456
expect(fixture.componentInstance.countEnd).toEqual(1);
457457
expect(fixture.componentInstance.source).toEqual(grid.columnList.toArray()[1]);
458458
expect(fixture.componentInstance.target).toEqual(grid.columnList.toArray()[0]);
459+
expect(fixture.componentInstance.cancel).toBe(false);
459460
}));
460461

461462
it('Should be able to cancel columnMoving event.', (async () => {
@@ -485,6 +486,35 @@ describe('IgxGrid - Column Moving #grid', () => {
485486
expect(columnsList[2].field).toEqual('LastName');
486487
}));
487488

489+
it('Should be able to cancel columnMovingEnd event.', (async () => {
490+
const headers: DebugElement[] = fixture.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
491+
492+
// step 1 - subscribe to the columnMovingEnd event in order to cancel it
493+
grid.columnMovingEnd.subscribe((e) => {
494+
if (fixture.componentInstance.target.field === 'Name') {
495+
e.cancel = true;
496+
}
497+
});
498+
499+
// step 2 - try moving a column
500+
const header = headers[0].nativeElement;
501+
UIInteractions.simulatePointerEvent('pointerdown', header, 150, 65);
502+
await wait();
503+
UIInteractions.simulatePointerEvent('pointermove', header, 156, 71);
504+
await wait();
505+
UIInteractions.simulatePointerEvent('pointermove', header, 330, 75);
506+
await wait(50);
507+
UIInteractions.simulatePointerEvent('pointerup', header, 330, 75);
508+
await wait(50);
509+
fixture.detectChanges();
510+
511+
// step 3 - verify the event was canceled(in componentInstance)
512+
const columnsList = grid.columnList.toArray();
513+
expect(columnsList[0].field).toEqual('ID');
514+
expect(columnsList[1].field).toEqual('Name');
515+
expect(columnsList[2].field).toEqual('LastName');
516+
}));
517+
488518
it('Should preserve filtering after columns are reordered.', async () => {
489519
pending('This scenario need to be reworked with new Filtering row');
490520
fixture.componentInstance.isFilterable = true;

0 commit comments

Comments
 (0)