Skip to content

Commit 5000fff

Browse files
authored
Merge pull request #10070 from IgniteUI/sstoychev/add-row-api
feat(add-row): implementing context-free beginAddRow method #9675 - master
2 parents 925d488 + a7abede commit 5000fff

12 files changed

+323
-27
lines changed

Diff for: CHANGELOG.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ All notable changes for each version of this project will be documented in this
66

77
### New Features
88
- `igxGrid`, `igxHierarchicalGrid`, `igxTreeGrid`
9+
- Added two public methods that spawn the add row UI for an arbitrary record in the current data view. One that accepts a rowID to use as the row the UI spawns under and the other accepting an index that has a distinct implementation for `IgxTreeGrid`. Please, refer to the official documentation for more information:[Grid Row Adding](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/row-adding) and [Tree Grid Row Adding](https://www.infragistics.com/products/ignite-ui-angular/angular/components/treegrid/row-adding).
10+
11+
_Note:_ That the new record is still added at the end of the data view, after the end-user submits it.
12+
```typescript
13+
this.grid.beginAddRowById('ALFKI'); // spawns the add row UI under the row with PK 'ALFKI'
14+
this.grid.beginAddRowById(null); // spawns the add row UI as the first record
15+
this.grid.beginAddRowByIndex(10); // spawns the add row UI at index 10
16+
this.grid.beginAddRowByIndex(0); // spawns the add row UI as the first record
17+
this.treeGrid.beginAddRowById('ALFKI', true); // spawns the add row UI to add a child for the row with PK 'ALFKI'
18+
this.treeGrid.beginAddRowByIndex(10, true); // spawns the add row UI to add a child for the row at index 10
19+
this.treeGrid.beginAddRowByIndex(null); // spawns the add row UI as the first record
20+
```
921
- Added capability to restore the state of multi column headers with `IgxGridStateDirective`.
1022

1123
## 12.1.3
@@ -69,7 +81,7 @@ All notable changes for each version of this project will be documented in this
6981
- `IgxGridCellComponent`, `IgxTreeGridCellComponent`, `IgxHierarchicalGridCellComponent` are no longer exposed in the public API. Instead, a new class `IgxGridCell` replaces all of these. It is a facade class which exposes only the public API of the above mentioned. Automatic migration will change these imports with `CellType`, which is the interface implemented by `IgxGridCell`
7082
- **Behavioral changes**
7183
- `getCellByKey`, `getCellByColumn`, `getCellByColumnVisibleIndex`, `row.cells`, `column.cells`, `grid.selectedCells` now return an `IgxGridCell` the `CellType` interface.
72-
- `cell` in `IGridCellEventArgs` is now `CellType`. `IGridCellEventArgs` are emitetd in `cellClick`, `selected`, `contextMenu` and `doubleClick` events.
84+
- `cell` in `IGridCellEventArgs` is now `CellType`. `IGridCellEventArgs` are emitted in `cellClick`, `selected`, `contextMenu` and `doubleClick` events.
7385
- `let-cell` property in cell template is now `CellType`.
7486
- `getCellByColumnVisibleIndex` is now deprecated and will be removed in next major version. Use `getCellByKey`, `getCellByColumn` instead.
7587

Diff for: projects/igniteui-angular/src/lib/grids/api.service.ts

+26
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,21 @@ export class GridBaseAPIService<T extends IgxGridBaseDirective & GridType> {
9696
return this.grid.rowList.find((row) => row.index === rowIndex);
9797
}
9898

99+
/**
100+
* Gets the rowID of the record at the specified data view index
101+
*
102+
* @param index
103+
* @param dataCollection
104+
*/
105+
public get_rec_id_by_index(index: number, dataCollection?: any[]): any {
106+
dataCollection = dataCollection || this.grid.data;
107+
if (index >= 0 && index < dataCollection.length) {
108+
const rec = dataCollection[index];
109+
return this.grid.primaryKey ? rec[this.grid.primaryKey] : rec;
110+
}
111+
return null;
112+
}
113+
99114
public get_cell_by_key(rowSelector: any, field: string): IgxGridCellComponent {
100115
const row = this.get_row_by_key(rowSelector);
101116
if (row && row.cells) {
@@ -393,6 +408,17 @@ export class GridBaseAPIService<T extends IgxGridBaseDirective & GridType> {
393408
return this.grid.primaryKey ? this.getRowData(rowID) : rowID;
394409
}
395410

411+
/**
412+
* Returns the index of the record in the data view by pk or -1 if not found or primaryKey is not set.
413+
*
414+
* @param pk
415+
* @param dataCollection
416+
*/
417+
public get_rec_index_by_id(pk: string | number, dataCollection?: any[]): number {
418+
dataCollection = dataCollection || this.grid.data;
419+
return this.grid.primaryKey ? dataCollection.findIndex(rec => rec[this.grid.primaryKey] === pk) : -1;
420+
}
421+
396422
public allow_expansion_state_change(rowID, expanded) {
397423
return this.grid.expansionStates.get(rowID) !== expanded;
398424
}

Diff for: projects/igniteui-angular/src/lib/grids/common/crud.service.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,8 @@ export class IgxRowAddCrudState extends IgxRowCrudState {
436436
* @hidden @internal
437437
*/
438438
public createAddRowParent(row: IgxRowDirective<IgxGridBaseDirective & GridType>, newRowAsChild?: boolean) {
439-
const rowIndex = row ? row.index : this.grid.rowList.length - 1;
440-
const rowId = row ? row.rowID : (rowIndex >= 0 ? this.grid.rowList.last.rowID : null);
441-
439+
const rowIndex = row ? row.index : -1;
440+
const rowId = row ? row.rowID : null;
442441
const isInPinnedArea = this.grid.isRecordPinnedByViewIndex(rowIndex);
443442
const pinIndex = this.grid.pinnedRecords.findIndex(x => x[this.primaryKey] === rowId);
444443
const unpinIndex = this.grid.getUnpinnedIndexById(rowId);
@@ -580,6 +579,7 @@ export class IgxGridCRUDService extends IgxRowAddCrudState {
580579
this.grid.navigateTo(this.row.index, -1);
581580
const dummyRow = this.grid.gridAPI.get_row_by_index(this.row.index);
582581
dummyRow.triggerAddAnimation();
582+
dummyRow.cdr.detectChanges();
583583
dummyRow.addAnimationEnd.pipe(first()).subscribe(() => {
584584
const cell = dummyRow.cells.find(c => c.editable);
585585
if (cell) {

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

+86
Original file line numberDiff line numberDiff line change
@@ -5966,6 +5966,92 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
59665966
this.crudService.endEdit(commit, event);
59675967
}
59685968

5969+
/**
5970+
* Enters add mode by spawning the UI under the specified row by rowID.
5971+
*
5972+
* @remarks
5973+
* If null is passed as rowID, the row adding UI is spawned as the first record in the data view
5974+
* @remarks
5975+
* Spawning the UI to add a child for a record only works if you provide a rowID
5976+
* @example
5977+
* ```typescript
5978+
* this.grid.beginAddRowById('ALFKI');
5979+
* this.grid.beginAddRowById('ALFKI', true);
5980+
* this.grid.beginAddRowById(null);
5981+
* ```
5982+
* @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
5983+
* @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
5984+
*/
5985+
public beginAddRowById(rowID: any, asChild?: boolean): void {
5986+
let index = rowID;
5987+
if (rowID == null) {
5988+
if (asChild) {
5989+
console.warn('The record cannot be added as a child to an unspecified record.');
5990+
return;
5991+
}
5992+
index = 0;
5993+
} else {
5994+
// find the index of the record with that PK
5995+
index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
5996+
rowID = index;
5997+
if (index === -1) {
5998+
console.warn('No row with the specified ID was found.');
5999+
return;
6000+
}
6001+
}
6002+
if (!this.dataView.length) {
6003+
this.beginAddRowForIndex(rowID, asChild);
6004+
return;
6005+
}
6006+
// check if the index is valid - won't support anything outside the data view
6007+
if (index >= 0 && index < this.dataView.length) {
6008+
// check if the index is in the view port
6009+
if ((index < this.virtualizationState.startIndex ||
6010+
index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6011+
!this.isRecordPinnedByViewIndex(index)) {
6012+
this.verticalScrollContainer.chunkLoad
6013+
.pipe(first(), takeUntil(this.destroy$))
6014+
.subscribe(() => {
6015+
this.beginAddRowForIndex(rowID, asChild);
6016+
});
6017+
this.navigateTo(index);
6018+
this.notifyChanges(true);
6019+
return;
6020+
}
6021+
this.beginAddRowForIndex(rowID, asChild);
6022+
} else {
6023+
console.warn('The row with the specified PK or index is outside of the current data view.');
6024+
}
6025+
}
6026+
6027+
/**
6028+
* Enters add mode by spawning the UI at the specified index.
6029+
*
6030+
* @remarks
6031+
* Accepted values for index are integers from 0 to this.grid.dataView.length
6032+
* @example
6033+
* ```typescript
6034+
* this.grid.beginAddRowByIndex(0);
6035+
* ```
6036+
* @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6037+
*/
6038+
public beginAddRowByIndex(index: number): void {
6039+
if (index === 0) {
6040+
return this.beginAddRowById(null);
6041+
}
6042+
return this.beginAddRowById(this.gridAPI.get_rec_id_by_index(index - 1, this.dataView));
6043+
}
6044+
6045+
protected beginAddRowForIndex(index: number, asChild: boolean = false) {
6046+
const row: IgxRowDirective<IgxGridBaseDirective & GridType> = index == null ?
6047+
null : this.rowList.find(r => r.index === index);
6048+
if (row !== undefined) {
6049+
this.crudService.enterAddRowMode(row, asChild);
6050+
} else {
6051+
console.warn('No row with the specified PK or index was found.');
6052+
}
6053+
}
6054+
59696055
protected switchTransactionService(val: boolean) {
59706056
if (val) {
59716057
this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);

Diff for: projects/igniteui-angular/src/lib/grids/grid/grid-add-row.spec.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IgxGridModule, IgxGridComponent } from './public_api';
22
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3-
import { TestBed, fakeAsync } from '@angular/core/testing';
3+
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
44
import { configureTestSuite } from '../../test-utils/configure-suite';
55
import { DebugElement } from '@angular/core';
66
import { GridFunctions, GridSummaryFunctions } from '../../test-utils/grid-functions.spec';
@@ -22,6 +22,8 @@ import { IgxGridRowComponent } from './grid-row.component';
2222
import { takeUntil, first } from 'rxjs/operators';
2323
import { Subject } from 'rxjs';
2424

25+
const DEBOUNCETIME = 30;
26+
2527
describe('IgxGrid - Row Adding #grid', () => {
2628
const GRID_ROW = 'igx-grid-row';
2729
const DISPLAY_CONTAINER = 'igx-display-container';
@@ -479,6 +481,35 @@ describe('IgxGrid - Row Adding #grid', () => {
479481

480482
expect(grid.gridAPI.get_row_by_index(1).addRowUI).toBeTrue();
481483
});
484+
485+
it('Should scroll and start adding a row as the first one when using the public API method', async () => {
486+
await wait(DEBOUNCETIME);
487+
fixture.detectChanges();
488+
489+
grid.navigateTo(20, 0);
490+
491+
await wait(DEBOUNCETIME);
492+
fixture.detectChanges();
493+
494+
grid.beginAddRowById(null);
495+
496+
await wait(DEBOUNCETIME);
497+
fixture.detectChanges();
498+
499+
expect(grid.gridAPI.get_row_by_index(0).addRowUI).toBeTrue();
500+
});
501+
502+
xit('Should scroll and start adding a row as for a row that is not in view', async () => {
503+
await wait(DEBOUNCETIME);
504+
fixture.detectChanges();
505+
506+
grid.beginAddRowById('FAMIA');
507+
508+
await wait(DEBOUNCETIME);
509+
fixture.detectChanges();
510+
511+
expect(grid.gridAPI.get_row_by_index(8).addRowUI).toBeTrue();
512+
});
482513
});
483514

484515
describe('Exit add row mode tests', () => {

Diff for: projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts

+26
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,32 @@ export class IgxTreeGridAPIService extends GridBaseAPIService<IgxTreeGridCompone
169169
return this.grid.records.get(rowID);
170170
}
171171

172+
/**
173+
* Gets the rowID of the record at the specified data view index
174+
*
175+
* @param index
176+
* @param dataCollection
177+
*/
178+
public get_rec_id_by_index(index: number, dataCollection?: any[]): any {
179+
dataCollection = dataCollection || this.grid.data;
180+
if (index >= 0 && index < dataCollection.length) {
181+
const rec = dataCollection[index];
182+
return this.grid.primaryKey ? rec.data[this.grid.primaryKey] : rec.data;
183+
}
184+
return null;
185+
}
186+
187+
/**
188+
* Returns the index of the record in the data view by pk or -1 if not found or primaryKey is not set.
189+
*
190+
* @param pk
191+
* @param dataCollection
192+
*/
193+
public get_rec_index_by_id(pk: string | number, dataCollection?: any[]): number {
194+
dataCollection = dataCollection || this.grid.data;
195+
return this.grid.primaryKey ? dataCollection.findIndex(rec => rec.data[this.grid.primaryKey] === pk) : -1;
196+
}
197+
172198
public addRowToData(data: any, parentRowID?: any) {
173199
if (parentRowID !== undefined && parentRowID !== null) {
174200

Diff for: projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts

+26
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,32 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy
605605
this.notifyChanges();
606606
}
607607

608+
/**
609+
* Enters add mode by spawning the UI with the context of the specified row by index.
610+
*
611+
* @remarks
612+
* Accepted values for index are integers from 0 to this.grid.dataView.length
613+
* @remarks
614+
* When adding the row as a child, the parent row is the specified row.
615+
* @remarks
616+
* To spawn the UI on top, call the function with index = null or a negative number.
617+
* In this case trying to add this row as a child will result in error.
618+
* @example
619+
* ```typescript
620+
* this.grid.beginAddRowByIndex(10);
621+
* this.grid.beginAddRowByIndex(10, true);
622+
* this.grid.beginAddRowByIndex(null);
623+
* ```
624+
* @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
625+
* @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
626+
*/
627+
public beginAddRowByIndex(index: number, asChild?: boolean): void {
628+
if (index === null || index < 0) {
629+
return this.beginAddRowById(null, asChild);
630+
}
631+
return this.beginAddRowById(this.gridAPI.get_rec_id_by_index(index, this.dataView), asChild);
632+
}
633+
608634
/**
609635
* @hidden
610636
*/

Diff for: src/app/grid-add-row/grid-add-row.sample.html

+11
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ <h1 igxCardHeaderTitle>Settings</h1>
4343
<igx-select [(ngModel)]="perPage">
4444
<igx-select-item *ngFor="let value of selectOptions" [value]="value">{{value}}</igx-select-item>
4545
</igx-select>
46+
<button igxButton="raised" (click)="beginAddRowAtIndex(indexInput.value)">Add Row At Index</button>
47+
<igx-input-group>
48+
<input igxInput name="index" type="number" #indexInput/>
49+
<label igxLabel for="index">Index</label>
50+
</igx-input-group>
51+
<button igxButton="raised" (click)="beginAddRowStart()">Add Row At Start</button>
52+
<igx-input-group>
53+
<input igxInput name="string" type="string" #stringInput value="CENTC"/>
54+
<label igxLabel for="string">PK</label>
55+
</igx-input-group>
56+
<button igxButton="raised" (click)="beginAddRowById(stringInput.value)">Add Row For ID</button>
4657
</igx-card-content>
4758
</igx-card>
4859
</div>

Diff for: src/app/grid-add-row/grid-add-row.sample.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import { Component, OnInit, ViewChild } from '@angular/core';
2+
import { IgxGridComponent } from 'igniteui-angular';
23

34
@Component({
45
selector: 'app-grid-add-row',
56
styleUrls: ['grid-add-row.sample.scss'],
67
templateUrl: `grid-add-row.sample.html`
78
})
89
export class GridAddRowSampleComponent implements OnInit {
10+
11+
@ViewChild(IgxGridComponent)
12+
public grid: IgxGridComponent;
13+
914
public data: any[];
1015
public dataFull: any[];
1116
public columns: any[];
@@ -69,4 +74,17 @@ export class GridAddRowSampleComponent implements OnInit {
6974
this.data = [];
7075
/* eslint-enable max-len */
7176
}
77+
78+
public beginAddRowAtIndex(index: string) {
79+
const numeric = parseInt(index, 10);
80+
this.grid.beginAddRowByIndex(numeric);
81+
}
82+
83+
public beginAddRowStart() {
84+
this.grid.beginAddRowById(null);
85+
}
86+
87+
public beginAddRowById(string: string) {
88+
this.grid.beginAddRowById(string);
89+
}
7290
}

0 commit comments

Comments
 (0)