Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 193b366

Browse files
committed
fix: Row Detail with inner grids
1 parent a18236b commit 193b366

17 files changed

+1222
-156
lines changed

docs/grid-functionalities/row-detail.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,4 +384,148 @@ addNewColumn() {
384384
// you could also use SlickGrid setColumns() method
385385
// this.angularGrid.slickGrid.setColumns(cols);
386386
}
387+
```
388+
389+
## Row Detail with Inner Grid
390+
391+
You can also add an inner grid inside a Row Detail, however there are a few things to know off and remember. Any time a Row Detail is falling outside the main grid viewport, it will be unmounted and until it comes back into the viewport which is then remounted. The process of unmounting and remounting means that Row Detail previous states aren't preserved, however you could use Grid State & Presets to overcome this problem.
392+
393+
##### Component
394+
395+
Main Grid Component
396+
397+
```ts
398+
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
399+
import { AngularGridInstance, Column, GridOption, GridState } from 'angular-slickgrid';
400+
401+
@Component({
402+
styleUrls: ['main-grid.component.scss'],
403+
templateUrl: './main-grid.component.html',
404+
encapsulation: ViewEncapsulation.None,
405+
})
406+
export class MainGridComponent implements OnInit {
407+
columnDefinitions: Column[] = [];
408+
gridOptions!: GridOption;
409+
angularGrid!: AngularGridInstance;
410+
dataset: Distributor[] = [];
411+
showSubTitle = true;
412+
413+
get rowDetailInstance(): SlickRowDetailView {
414+
return this.angularGrid.extensions.rowDetailView?.instance || {};
415+
}
416+
417+
angularGridReady(angularGrid: AngularGridInstance) {
418+
this.angularGrid = angularGrid;
419+
}
420+
421+
ngOnInit(): void {
422+
this.defineGrid();
423+
this.dataset = this.getData();
424+
}
425+
426+
defineGrid() {
427+
this.columnDefinitions = [ /*...*/ ];
428+
this.gridOptions = {
429+
enableRowDetailView: true,
430+
rowSelectionOptions: {
431+
selectActiveRow: true
432+
},
433+
preRegisterExternalExtensions: (pubSubService) => {
434+
// Row Detail View is a special case because of its requirement to create extra column definition dynamically
435+
// so it must be pre-registered before SlickGrid is instantiated, we can do so via this option
436+
const rowDetail = new SlickRowDetailView(pubSubService as EventPubSubService);
437+
return [{ name: ExtensionName.rowDetailView, instance: rowDetail }];
438+
},
439+
rowDetailView: {
440+
process: (item: any) => simulateServerAsyncCall(item),
441+
loadOnce: false, // IMPORTANT, you can't use loadOnce with inner grid because only HTML template are re-rendered, not JS events
442+
panelRows: 10,
443+
preloadComponent: PreloadComponent,
444+
viewComponent: InnerGridComponent,
445+
},
446+
};
447+
}
448+
}
449+
```
450+
451+
Now, let's define our Inner Grid Component
452+
453+
```html
454+
<div [class]="innerGridClass">
455+
<h4>Order Details (id: {{ model.id }})</h4>
456+
<div class="container-fluid">
457+
<angular-slickgrid
458+
[gridId]="innerGridId"
459+
[columnDefinitions]="innerColDefs"
460+
[gridOptions]="innerGridOptions"
461+
[dataset]="innerDataset"
462+
(onAngularGridCreated)="angularGridReady($event.detail)"
463+
>
464+
</angular-slickgrid>
465+
</div>
466+
</div>
467+
```
468+
469+
```ts
470+
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
471+
import { AngularGridInstance, Column, GridOption, GridState } from 'angular-slickgrid';
472+
473+
export interface Distributor { /* ... */ }
474+
export interface OrderData { /* ... */ }
475+
476+
@Component({
477+
templateUrl: './inner-grid.component.html',
478+
})
479+
export class InnerGridComponent implements OnInit {
480+
model!: Distributor;
481+
innerColDefs: Column[] = [];
482+
innerGridOptions!: GridOption;
483+
angularGrid!: AngularGridInstance;
484+
innerDataset: any[] = [];
485+
innerGridId = '';
486+
innerGridClass = '';
487+
488+
ngOnInit(): void {
489+
this.innerGridId = `innergrid-${this.model.id}`;
490+
this.innerGridClass = `row-detail-${this.model.id}`;
491+
this.defineGrid();
492+
this.innerDataset = [...this.model.orderData];
493+
}
494+
495+
angularGridReady(angularGrid: AngularGridInstance) {
496+
this.angularGrid = angularGrid;
497+
}
498+
499+
defineGrid() {
500+
// OPTIONALLY reapply Grid State as Presets before unmounting the compoment
501+
let gridState: GridState | undefined;
502+
const gridStateStr = sessionStorage.getItem(`gridstate_${this.innerGridClass}`);
503+
if (gridStateStr) {
504+
gridState = JSON.parse(gridStateStr);
505+
}
506+
507+
this.innerColDefs = [
508+
{ id: 'orderId', field: 'orderId', name: 'Order ID', filterable: true, sortable: true },
509+
{ id: 'shipCity', field: 'shipCity', name: 'Ship City', filterable: true, sortable: true },
510+
{ id: 'freight', field: 'freight', name: 'Freight', filterable: true, sortable: true, type: 'number' },
511+
{ id: 'shipName', field: 'shipName', name: 'Ship Name', filterable: true, sortable: true },
512+
];
513+
514+
this.innerGridOptions = {
515+
autoResize: {
516+
container: `.${this.innerGridClass}`,
517+
},
518+
enableFiltering: true,
519+
enableSorting: true,
520+
datasetIdPropertyName: 'orderId',
521+
presets: gridState, // reapply grid state presets
522+
};
523+
}
524+
525+
// OPTIONALLY save Grid State before unmounting the compoment
526+
handleBeforeGridDestroy() {
527+
const gridState = this.angularGrid.gridStateService.getCurrentGridState();
528+
sessionStorage.setItem(`gridstate_${this.innerGridClass}`, JSON.stringify(gridState));
529+
}
530+
}
387531
```

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@
5353
},
5454
"dependencies": {
5555
"@ngx-translate/core": "^15.0.0",
56-
"@slickgrid-universal/common": "~5.12.2",
57-
"@slickgrid-universal/custom-footer-component": "~5.12.2",
58-
"@slickgrid-universal/empty-warning-component": "~5.12.2",
59-
"@slickgrid-universal/event-pub-sub": "~5.12.2",
60-
"@slickgrid-universal/pagination-component": "~5.12.2",
61-
"@slickgrid-universal/row-detail-view-plugin": "~5.12.2",
62-
"@slickgrid-universal/rxjs-observable": "~5.12.2",
56+
"@slickgrid-universal/common": "~5.13.0",
57+
"@slickgrid-universal/custom-footer-component": "~5.13.0",
58+
"@slickgrid-universal/empty-warning-component": "~5.13.0",
59+
"@slickgrid-universal/event-pub-sub": "~5.13.0",
60+
"@slickgrid-universal/pagination-component": "~5.13.0",
61+
"@slickgrid-universal/row-detail-view-plugin": "~5.13.0",
62+
"@slickgrid-universal/rxjs-observable": "~5.13.0",
6363
"dequal": "^2.0.3",
6464
"rxjs": "^7.8.1"
6565
},
@@ -92,12 +92,12 @@
9292
"@ngx-translate/http-loader": "^8.0.0",
9393
"@popperjs/core": "^2.11.8",
9494
"@release-it/conventional-changelog": "^10.0.0",
95-
"@slickgrid-universal/composite-editor-component": "~5.12.2",
96-
"@slickgrid-universal/custom-tooltip-plugin": "~5.12.2",
97-
"@slickgrid-universal/excel-export": "~5.12.2",
98-
"@slickgrid-universal/graphql": "~5.12.2",
99-
"@slickgrid-universal/odata": "~5.12.2",
100-
"@slickgrid-universal/text-export": "~5.12.2",
95+
"@slickgrid-universal/composite-editor-component": "~5.13.0",
96+
"@slickgrid-universal/custom-tooltip-plugin": "~5.13.0",
97+
"@slickgrid-universal/excel-export": "~5.13.0",
98+
"@slickgrid-universal/graphql": "~5.13.0",
99+
"@slickgrid-universal/odata": "~5.13.0",
100+
"@slickgrid-universal/text-export": "~5.13.0",
101101
"@types/fnando__sparkline": "^0.3.7",
102102
"@types/jest": "^29.5.14",
103103
"@types/node": "^22.13.1",

src/app/app-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { GridTreeDataParentChildComponent } from './examples/grid-tree-data-pare
4242
import { Grid18Component } from './examples/grid18.component';
4343
import { Grid43Component } from './examples/grid43.component';
4444
import { Grid44Component } from './examples/grid44.component';
45+
import { Grid45Component } from './examples/grid45.component';
4546
import { SwtCommonGridTestComponent } from './examples/swt-common-grid-test.component';
4647

4748
import { NgModule } from '@angular/core';
@@ -86,6 +87,7 @@ const routes: Routes = [
8687
{ path: 'range', component: GridRangeComponent },
8788
{ path: 'resize-by-content', component: GridResizeByContentComponent },
8889
{ path: 'rowdetail', component: GridRowDetailComponent },
90+
{ path: 'rowdetail-innergrid', component: Grid45Component },
8991
{ path: 'rowmove', component: GridRowMoveComponent },
9092
{ path: 'rowspan-timesheets', component: Grid43Component },
9193
{ path: 'rowspan-large', component: Grid44Component },

src/app/app.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@
182182
<li class="nav-item">
183183
<a class="nav-link" routerLinkActive="active" [routerLink]="['/rowspan-large']">44- Colspan/Rowspan (large data)</a>
184184
</li>
185+
<li class="nav-item">
186+
<a class="nav-link" routerLinkActive="active" [routerLink]="['/rowdetail-innergrid']">45- Row Detail with inner Grid</a>
187+
</li>
185188
</ul>
186189
</section>
187190

src/app/app.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ import { GridTreeDataParentChildComponent } from './examples/grid-tree-data-pare
5858
import { Grid18Component } from './examples/grid18.component';
5959
import { Grid43Component } from './examples/grid43.component';
6060
import { Grid44Component } from './examples/grid44.component';
61+
import { Grid45Component } from './examples/grid45.component';
6162
import { HomeComponent } from './examples/home.component';
6263
import { CustomPagerComponent } from './examples/grid-custom-pager.component';
6364
import { RowDetailPreloadComponent } from './examples/rowdetail-preload.component';
6465
import { RowDetailViewComponent } from './examples/rowdetail-view.component';
66+
import { Grid45DetailComponent } from './examples/grid45-detail.component';
6567

6668
import { SwtCommonGridTestComponent } from './examples/swt-common-grid-test.component';
6769
import { SwtCommonGridPaginationComponent } from './examples/swt-common-grid-pagination.component';
@@ -149,6 +151,8 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
149151
Grid18Component,
150152
Grid43Component,
151153
Grid44Component,
154+
Grid45Component,
155+
Grid45DetailComponent,
152156
RowDetailPreloadComponent,
153157
RowDetailViewComponent,
154158
SwtCommonGridTestComponent,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div [class]="innerGridClass">
2+
<h4>{{ model.companyName }} - Order Details (id: {{ model.id }})</h4>
3+
<div class="container-fluid">
4+
<angular-slickgrid
5+
[gridId]="innerGridId"
6+
[columnDefinitions]="innerColDefs"
7+
[gridOptions]="innerGridOptions"
8+
[dataset]="innerDataset"
9+
(onAngularGridCreated)="angularGridReady($event.detail)"
10+
(onBeforeGridDestroy)="handleBeforeGridDestroy()"
11+
>
12+
</angular-slickgrid>
13+
</div>
14+
</div>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
2+
import { AngularGridInstance, Column, GridOption, GridState } from '../modules/angular-slickgrid';
3+
4+
export interface Distributor {
5+
id: number;
6+
companyId: number;
7+
companyName: string;
8+
city: string;
9+
streetAddress: string;
10+
zipCode: string;
11+
country: string;
12+
orderData: OrderData[];
13+
isUsingInnerGridStatePresets: boolean;
14+
}
15+
16+
export interface OrderData {
17+
orderId: string;
18+
shipCity: string;
19+
freight: number;
20+
shipName: string;
21+
}
22+
23+
@Component({
24+
styles: ['.detail-label { display: inline-flex; align-items: center; gap: 4px; padding: 4px; }', 'label { font-weight: 600; }'],
25+
templateUrl: './grid45-detail.component.html',
26+
encapsulation: ViewEncapsulation.None,
27+
})
28+
export class Grid45DetailComponent implements OnInit {
29+
model!: Distributor;
30+
innerColDefs: Column[] = [];
31+
innerGridOptions!: GridOption;
32+
angularGrid!: AngularGridInstance;
33+
innerDataset: any[] = [];
34+
innerGridId = '';
35+
innerGridClass = '';
36+
showGrid = false;
37+
38+
ngOnInit(): void {
39+
this.innerGridId = `innergrid-${this.model.id}`;
40+
this.innerGridClass = `row-detail-${this.model.id}`;
41+
// define the grid options & columns and then create the grid itself
42+
this.defineGrid();
43+
44+
// mock some data (different in each dataset)
45+
this.innerDataset = [...this.model.orderData];
46+
this.showGrid = true;
47+
}
48+
49+
angularGridReady(angularGrid: AngularGridInstance) {
50+
this.angularGrid = angularGrid;
51+
}
52+
53+
defineGrid() {
54+
// when Grid State found in Session Storage, reapply inner Grid State then reapply it as preset
55+
let gridState: GridState | undefined;
56+
if (this.model.isUsingInnerGridStatePresets) {
57+
const gridStateStr = sessionStorage.getItem(`gridstate_${this.innerGridClass}`);
58+
if (gridStateStr) {
59+
gridState = JSON.parse(gridStateStr);
60+
}
61+
}
62+
63+
this.innerColDefs = [
64+
{ id: 'orderId', field: 'orderId', name: 'Order ID', filterable: true, sortable: true },
65+
{ id: 'shipCity', field: 'shipCity', name: 'Ship City', filterable: true, sortable: true },
66+
{ id: 'freight', field: 'freight', name: 'Freight', filterable: true, sortable: true, type: 'number' },
67+
{ id: 'shipName', field: 'shipName', name: 'Ship Name', filterable: true, sortable: true },
68+
];
69+
70+
this.innerGridOptions = {
71+
autoResize: {
72+
container: `.${this.innerGridClass}`,
73+
rightPadding: 30,
74+
minHeight: 200,
75+
},
76+
enableFiltering: true,
77+
enableSorting: true,
78+
rowHeight: 33,
79+
enableCellNavigation: true,
80+
datasetIdPropertyName: 'orderId',
81+
presets: gridState,
82+
};
83+
}
84+
85+
handleBeforeGridDestroy() {
86+
if (this.model.isUsingInnerGridStatePresets) {
87+
const gridState = this.angularGrid.gridStateService.getCurrentGridState();
88+
sessionStorage.setItem(`gridstate_${this.innerGridClass}`, JSON.stringify(gridState));
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)