diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 5225316ec..786ec157d 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -40,6 +40,7 @@ import { GridTabsComponent } from './examples/grid-tabs.component';
import { GridTradingComponent } from './examples/grid-trading.component';
import { GridTreeDataHierarchicalComponent } from './examples/grid-tree-data-hierarchical.component';
import { GridTreeDataParentChildComponent } from './examples/grid-tree-data-parent-child.component';
+import { Grid43Component } from './examples/grid43.component';
import { SwtCommonGridTestComponent } from './examples/swt-common-grid-test.component';
import { NgModule } from '@angular/core';
@@ -58,6 +59,7 @@ const routes: Routes = [
{ path: 'context', component: GridContextMenuComponent },
{ path: 'custom-pagination', component: GridCustomPaginationComponent },
{ path: 'custom-tooltip', component: GridCustomTooltipComponent },
+ { path: 'csv-grid', component: Grid43Component },
{ path: 'drag-recycle', component: GridDragRecycleComponent },
{ path: 'editor', component: GridEditorComponent },
{ path: 'excel-formula', component: GridExcelFormulaComponent },
diff --git a/src/app/app.component.html b/src/app/app.component.html
index c94169b1d..0112e173a 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -201,6 +201,11 @@
42- Custom Pagination
+
+
+ 43- Create Grid from CSV
+
+
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 49dfdbc67..0bcb6c38d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -56,6 +56,7 @@ import { GridTabsComponent } from './examples/grid-tabs.component';
import { GridTradingComponent } from './examples/grid-trading.component';
import { GridTreeDataHierarchicalComponent } from './examples/grid-tree-data-hierarchical.component';
import { GridTreeDataParentChildComponent } from './examples/grid-tree-data-parent-child.component';
+import { Grid43Component } from './examples/grid43.component';
import { HomeComponent } from './examples/home.component';
import { CustomPagerComponent } from './examples/grid-custom-pager.component';
import { RowDetailPreloadComponent } from './examples/rowdetail-preload.component';
@@ -144,6 +145,7 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
GridTradingComponent,
GridTreeDataParentChildComponent,
GridTreeDataHierarchicalComponent,
+ Grid43Component,
RowDetailPreloadComponent,
RowDetailViewComponent,
SwtCommonGridTestComponent,
diff --git a/src/app/examples/grid43.component.html b/src/app/examples/grid43.component.html
new file mode 100644
index 000000000..51875a6b7
--- /dev/null
+++ b/src/app/examples/grid43.component.html
@@ -0,0 +1,55 @@
+
+
+ Example 43: Dynamically Create Grid from CSV / Excel import
+
+
+ code
+
+
+
+
+
+
+
+
+ Allow creating a grid dynamically by importing an external CSV or Excel file. This script demo will read the CSV file and will
+ consider the first row as the column header and create the column definitions accordingly, while the next few rows will be
+ considered the dataset. Note that this example is demoing a CSV file import but in your application you could easily implemnt
+ an Excel file uploading.
+
+
+
A default CSV file can be download
here .
+
+
+
+ Choose a CSV fileā¦
+
+
+
or
+
+
+ Use default CSV data
+
+ Destroy Grid
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/examples/grid43.component.ts b/src/app/examples/grid43.component.ts
new file mode 100644
index 000000000..f76332c59
--- /dev/null
+++ b/src/app/examples/grid43.component.ts
@@ -0,0 +1,108 @@
+import { ChangeDetectorRef, Component, ViewEncapsulation } from '@angular/core';
+import { ExcelExportService } from '@slickgrid-universal/excel-export';
+import { type Column, type GridOption, toCamelCase } from './../modules/angular-slickgrid';
+
+const sampleDataRoot = 'assets/data';
+
+@Component({
+ styles: ['.file-upload { max-width: 300px; }'],
+ encapsulation: ViewEncapsulation.None,
+ templateUrl: './grid43.component.html'
+})
+export class Grid43Component {
+ columnDefinitions: Column[] = [];
+ gridOptions!: GridOption;
+ dataset: any[] = [];
+ gridCreated = false;
+ showSubTitle = true;
+ uploadFileRef = '';
+ templateUrl = `${sampleDataRoot}/users.csv`;
+
+ constructor(private readonly cd: ChangeDetectorRef) { }
+
+ handleFileImport(event: any) {
+ const file = event.target.files[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e: any) => {
+ const content = e.target.result;
+ this.dynamicallyCreateGrid(content);
+ };
+ reader.readAsText(file);
+ }
+ }
+
+ handleDefaultCsv() {
+ const staticDataCsv = `First Name,Last Name,Age,Type\nBob,Smith,33,Teacher\nJohn,Doe,20,Student\nJane,Doe,21,Student`;
+ this.dynamicallyCreateGrid(staticDataCsv);
+ this.uploadFileRef = '';
+ }
+
+ destroyGrid() {
+ this.gridCreated = false;
+ }
+
+ dynamicallyCreateGrid(csvContent: string) {
+ // dispose of any previous grid before creating a new one
+ this.gridCreated = false;
+ this.cd.detectChanges();
+
+ const dataRows = csvContent?.split('\n');
+ const colDefs: Column[] = [];
+ const outputData: any[] = [];
+
+ // create column definitions
+ dataRows.forEach((dataRow, rowIndex) => {
+ const cellValues = dataRow.split(',');
+ const dataEntryObj: any = {};
+
+ if (rowIndex === 0) {
+ // the 1st row is considered to be the header titles, we can create the column definitions from it
+ for (const cellVal of cellValues) {
+ const camelFieldName = toCamelCase(cellVal);
+ colDefs.push({
+ id: camelFieldName,
+ name: cellVal,
+ field: camelFieldName,
+ filterable: true,
+ sortable: true,
+ });
+ }
+ } else {
+ // at this point all column defs were created and we can loop through them and
+ // we can now start adding data as an object and then simply push it to the dataset array
+ cellValues.forEach((cellVal, colIndex) => {
+ dataEntryObj[colDefs[colIndex].id] = cellVal;
+ });
+
+ // a unique "id" must be provided, if not found then use the row index and push it to the dataset
+ if ('id' in dataEntryObj) {
+ outputData.push(dataEntryObj);
+ } else {
+ outputData.push({ ...dataEntryObj, id: rowIndex });
+ }
+ }
+ });
+
+ this.gridOptions = {
+ gridHeight: 300,
+ gridWidth: 800,
+ enableFiltering: true,
+ enableExcelExport: true,
+ externalResources: [new ExcelExportService()],
+ headerRowHeight: 35,
+ rowHeight: 33,
+ };
+
+ this.dataset = outputData;
+ this.columnDefinitions = colDefs;
+ this.gridCreated = true;
+ this.cd.detectChanges();
+ }
+
+ toggleSubTitle() {
+ this.showSubTitle = !this.showSubTitle;
+ const action = this.showSubTitle ? 'remove' : 'add';
+ document.querySelector('.subtitle')?.classList[action]('hidden');
+ }
+}
diff --git a/src/assets/data/users.csv b/src/assets/data/users.csv
new file mode 100644
index 000000000..0d81e74e9
--- /dev/null
+++ b/src/assets/data/users.csv
@@ -0,0 +1,5 @@
+First Name,Last Name,Age,User Type
+John,Doe,20,Student
+Bob,Smith,33,Assistant Teacher
+Jane,Doe,21,Student
+Robert,Ken,42,Teacher
diff --git a/test/cypress/e2e/example43.cy.ts b/test/cypress/e2e/example43.cy.ts
new file mode 100644
index 000000000..37c7d01c3
--- /dev/null
+++ b/test/cypress/e2e/example43.cy.ts
@@ -0,0 +1,88 @@
+describe('Example 43 - Dynamically Create Grid from CSV / Excel import', () => {
+ const defaultCsvTitles = ['First Name', 'Last Name', 'Age', 'Type'];
+ const GRID_ROW_HEIGHT = 33;
+
+ it('should display Example title', () => {
+ cy.visit(`${Cypress.config('baseUrl')}/csv-grid`);
+ cy.get('h2').should('contain', 'Example 43: Dynamically Create Grid from CSV / Excel import');
+ });
+
+ it('should load default CSV file and expect default column titles', () => {
+ cy.get('[data-test="static-data-btn"]')
+ .click();
+
+ cy.get('.slick-header-columns')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(defaultCsvTitles[index]));
+ });
+
+ it('should expect default data in the grid', () => {
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Bob');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Smith');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(2)`).should('contain', '33');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(3)`).should('contain', 'Teacher');
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'John');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(1)`).should('contain', 'Doe');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(2)`).should('contain', '20');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(3)`).should('contain', 'Student');
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Jane');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(1)`).should('contain', 'Doe');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(2)`).should('contain', '21');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(3)`).should('contain', 'Student');
+ });
+
+ it('should sort by "Age" and expect it to be sorted in ascending order', () => {
+ cy.get('.slick-header-columns .slick-header-column:nth(2)')
+ .click();
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'John');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Doe');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(2)`).should('contain', '20');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(3)`).should('contain', 'Student');
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Jane');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(1)`).should('contain', 'Doe');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(2)`).should('contain', '21');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(3)`).should('contain', 'Student');
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Bob');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(1)`).should('contain', 'Smith');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(2)`).should('contain', '33');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(3)`).should('contain', 'Teacher');
+ });
+
+ it('should click again the "Age" column and expect it to be sorted in descending order', () => {
+ cy.get('.slick-header-columns .slick-header-column:nth(2)')
+ .click();
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Bob');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Smith');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(2)`).should('contain', '33');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(3)`).should('contain', 'Teacher');
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Jane');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(1)`).should('contain', 'Doe');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(2)`).should('contain', '21');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(3)`).should('contain', 'Student');
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'John');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(1)`).should('contain', 'Doe');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(2)`).should('contain', '20');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(3)`).should('contain', 'Student');
+ });
+
+ it('should filter Smith as "Last Name" and expect only 1 row in the grid', () => {
+ cy.get('.slick-headerrow .slick-headerrow-column:nth(1) input')
+ .type('Smith');
+
+ cy.get('.slick-row')
+ .should('have.length', 1);
+
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Bob');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Smith');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(2)`).should('contain', '33');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(3)`).should('contain', 'Teacher');
+ });
+});