HTML tables are a good starting point when a page only needs to show a few rows of static data. The moment users expect real application behavior - sorting, editing, column movement, keyboard navigation, virtualization, import/export, and consistent styling - the “just use a table” approach becomes a feature project of its own.
That is why many Angular teams reach for a data table or grid component. A smart data table is not just a prettier <table>; it is a UI layer for working with large, interactive datasets without forcing developers to rebuild grid infrastructure on every screen.
In this tutorial, we will start with the capabilities developers usually expect from a smart Angular table, wire up an open-source Angular table, and then rebuild the same scenario with Wijmo’s FlexGrid to show where a purpose-built enterprise grid reduces custom code.
We will explore:
- What Developers Expect from a Smart Angular Data Table
- Using Angular Data Tables
- Creating an Angular Application
- Adding an Open-Source Angular Data Table
- Custom Cell Editing in ngx-datatable
- When to Choose a Commercial Data Component
- Building an Angular Data Table with Wijmo’s FlexGrid
- Editing Angular DataGrid Cell Values
- Sorting and Reordering Columns in an Angular DataGrid
- Importing and Exporting Data in an Angular DataGrid
Ready to get started? Download Wijmo Today!
What Developers Expect from a Smart Angular Data Table
A smart table should help users explore, edit, and move data without making the application code harder to maintain. Common requirements include:
- Data binding to arrays, APIs, databases, or remote services.
- Sorting, filtering, searching, grouping, and paging.
- Virtualization or incremental loading for large datasets.
- Column resizing, reordering, pinning/freezing, and show/hide behavior.
- Inline editing, validation, keyboard support, and copy/paste behavior.
- Import/export workflows, especially Excel .xlsx in business applications.
- Responsive layouts and cell-level visualizations such as templates, icons, progress indicators, and sparklines.
Open-source table libraries can be enough for many screens, especially when the requirements are narrow. Enterprise applications usually need stronger guarantees: consistent behavior across teams, framework wrappers, documentation, support, accessibility, and predictable upgrade paths.
Using Angular Data Tables
Angular gives teams a structured, TypeScript-first foundation for building single-page applications. That structure is useful in enterprise apps, but it also means UI components need to fit Angular conventions instead of fighting them. A good Angular table should work cleanly with component templates, typed data models, and Angular’s build pipeline.
For an open-source baseline, we will use ngx-datatable, an Angular component for large and complex data, with features such as virtual DOM handling, templates, horizontal and vertical scrolling, column reordering and resizing, pagination, sorting, and row selection.
Create the Angular Application
Start with a new standalone Angular application:
npx -p @angular/cli ng new smart-angular-data-table --standalone --routing=false --style=css
cd smart-angular-data-table
npm start
The application should appear as follows:

Create a small sales dataset that both table demos can share. Place it in src/app/data/recent-sales.ts:
export interface Sale {
id: number;
country: string;
soldBy: string;
client: string;
description: string;
value: number;
itemCount: number;
}
export const recentSales: Sale[] = [
{ id: 1, country: 'Canada', soldBy: 'Bill', client: 'Cerberus Corp.', description: 'Prothean artifacts', value: 6250, itemCount: 50 },
{ id: 2, country: 'Canada', soldBy: 'Bill', client: 'Strickland Propane', description: 'Propane and propane accessories', value: 2265, itemCount: 20 },
{ id: 3, country: 'USA', soldBy: 'Ted', client: 'Dunder Mifflin', description: 'Assorted paper-making supplies', value: 4700, itemCount: 10 },
{ id: 4, country: 'USA', soldBy: 'Ted', client: 'Utopia Planitia Shipyards', description: 'Dilithium, duranium, assorted shipbuilding supplies', value: 21750, itemCount: 250 },
{ id: 5, country: 'USA', soldBy: 'Ted', client: 'Glengarry Estates', description: 'Desks, phones, coffee, steak knives, and one Cadillac', value: 5000, itemCount: 5 },
{ id: 6, country: 'Germany', soldBy: 'Angela', client: 'Wayne Enterprises', description: 'Suit armor and run-flat tires', value: 35000, itemCount: 25 },
{ id: 7, country: 'Germany', soldBy: 'Angela', client: 'Stark Industries', description: 'Armor and rocket fuel', value: 25000, itemCount: 10 },
{ id: 8, country: 'Germany', soldBy: 'Angela', client: 'Nakatomi Trading Corp.', description: 'Fire extinguishers and replacement windows', value: 15000, itemCount: 50 },
{ id: 9, country: 'UK', soldBy: 'Jill', client: 'Spaceley Sprockets', description: 'Anti-gravity propulsion units', value: 25250, itemCount: 50 },
{ id: 10, country: 'UK', soldBy: 'Jill', client: 'General Products', description: 'Ion engines', value: 33200, itemCount: 40 }
];
Add an Open-Source Angular Data Table
Install ngx-datatable and Bootstrap:
npm i @swimlane/ngx-datatable bootstrap
Then, create a standalone component for the open-source version. The component imports NgxDatatableModule directly and binds the shared sales array to the table rows:
import { Component } from '@angular/core';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { recentSales } from '../data/recent-sales';
@Component({
selector: 'app-open-source-demo',
standalone: true,
imports: [NgxDatatableModule],
styleUrl: './open-source-demo.css',
templateUrl: './open-source-demo.html'
})
export class OpenSourceDemoComponent {
rows = recentSales;
editing: Record<string, boolean> = {};
updateValue(event: Event, field: string, rowIndex: number): void {
const value = (event.target as HTMLInputElement).value;
this.editing[`${rowIndex}-${field}`] = false;
this.rows[rowIndex] = { ...this.rows[rowIndex], [field]: value };
this.rows = [...this.rows];
}
}
The initial table markup is straightforward:
<ngx-datatable class="bootstrap" [rows]="rows" [headerHeight]="50" [rowHeight]="40">
<ngx-datatable-column name="ID" prop="id"></ngx-datatable-column>
<ngx-datatable-column name="Country" prop="country"></ngx-datatable-column>
<ngx-datatable-column name="Sold By" prop="soldBy"></ngx-datatable-column>
<ngx-datatable-column name="Client" prop="client"></ngx-datatable-column>
<ngx-datatable-column name="Description" prop="description"></ngx-datatable-column>
<ngx-datatable-column name="Value" prop="value"></ngx-datatable-column>
<ngx-datatable-column name="Item Count" prop="itemCount"></ngx-datatable-column>
</ngx-datatable>
The sales data should now display when you run the application:

Custom Cell Editing in ngx-datatable
To make a cell editable, you can replace the plain column with a cell template that toggles between a display span and an input. The pattern works, but it is custom code that you must repeat or abstract for every editable field:
<ngx-datatable-column name="Client" prop="client">
<ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value">
<span
title="Double-click to edit"
*ngIf="!editing[rowIndex + '-client']"
(dblclick)="editing[rowIndex + '-client'] = true">
{{ value }}
</span>
<input
*ngIf="editing[rowIndex + '-client']"
autofocus
type="text"
[value]="value"
(blur)="updateValue($event, 'client', rowIndex)" />
</ng-template>
</ngx-datatable-column>
These changes add support for editing the Client and Description columns in ngx-datatable, with changes automatically persisted to the underlying data source.

This is the trade-off: ngx-datatable gives you a flexible baseline, but richer interaction often becomes custom Angular template code. That may be acceptable for one table. Across many business screens, the maintenance cost grows quickly.
When to Choose a Commercial Data Component
MESCIUS offers several JavaScript data products that target different data experiences:
| Product | Best Fit | Highlights |
| SpreadJS | Excel-like spreadsheet applications | JavaScript spreadsheet experiences with Excel import/export, a high-speed calculation engine, dashboards, forms, reports, and 500+ Excel functions. |
| DataViewsJS | Data presentation beyond a traditional grid | Tree, card, masonry, trellis, timeline, Gantt, calendar, and grid layouts, with Angular, React, and Vue wrappers. |
| Wijmo FlexGrid | High-performance Angular data grid scenarios | Excel-like grid behavior, templates, virtualization, sorting, editing, grouping, filtering, import/export, and framework support for Angular, React, Vue, and PureJS. |
Build the Same Angular Data Table with Wijmo FlexGrid
Now, let us replace the table with Wijmo FlexGrid. The important difference is that common grid interactions - editing, sorting, resizing, and column dragging - are built into the grid instead of being hand-coded in cell templates.
Install the packages needed for the grid demo:
npm i @mescius/wijmo.angular2.grid @mescius/wijmo.styles
npm i @mescius/wijmo.grid.xlsx jszip
Add the Wijmo stylesheet. You can place this in styles.css or the component stylesheet:
@import "@mescius/wijmo.styles/wijmo.css";
@import "bootstrap/dist/css/bootstrap.css";
Create a standalone Angular datagrid component using FlexGrid:
import { Component, ViewChild } from '@angular/core';
import * as wjcGrid from '@mescius/wijmo.grid';
import { WjGridModule } from '@mescius/wijmo.angular2.grid';
import { FlexGridXlsxConverter } from '@mescius/wijmo.grid.xlsx';
import { recentSales, Sale } from '../data/recent-sales';
@Component({
selector: 'app-flexgrid-demo',
standalone: true,
imports: [WjGridModule],
styleUrl: './flexgrid-demo.css',
templateUrl: './flexgrid-demo.html'
})
export class FlexGridDemoComponent {
@ViewChild('flex') flex?: wjcGrid.FlexGrid;
sales: Sale[] = recentSales;
load(input: HTMLInputElement): void {
const file = input.files?.[0];
if (!file || !this.flex) return;
FlexGridXlsxConverter.loadAsync(this.flex, file, {
includeColumnHeaders: true
});
input.value = '';
}
save(): void {
if (!this.flex) return;
FlexGridXlsxConverter.saveAsync(
this.flex,
{ includeColumnHeaders: true, includeCellStyles: false },
'FlexGrid.xlsx'
);
}
}
Use explicit columns so the grid order, widths, and formatting are easy to scan:
<div class="card main-panel">
<div class="card-header"><h1>Wijmo FlexGrid Demo</h1></div>
<div class="card-body">
<wj-flex-grid #flex [itemsSource]="sales" [autoGenerateColumns]="false">
<wj-flex-grid-column header="ID" binding="id" [width]="60" [isReadOnly]="true"></wj-flex-grid-column>
<wj-flex-grid-column header="Client" binding="client" [width]="150"></wj-flex-grid-column>
<wj-flex-grid-column header="Description" binding="description" [width]="320"></wj-flex-grid-column>
<wj-flex-grid-column header="Value" binding="value" format="n2" [width]="110"></wj-flex-grid-column>
<wj-flex-grid-column header="Quantity" binding="itemCount" format="n0" [width]="100"></wj-flex-grid-column>
<wj-flex-grid-column header="Sold By" binding="soldBy" [width]="100"></wj-flex-grid-column>
<wj-flex-grid-column header="Country" binding="country" [width]="100"></wj-flex-grid-column>
</wj-flex-grid>
</div>
<div class="card-footer">
<input #importFile type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
<button type="button" (click)="load(importFile)">Import</button>
<button type="button" (click)="save()">Export</button>
</div>
</div>
When you run the application, the sales data is displayed in the FlexGrid:

Editing Grid Cell Values with FlexGrid
FlexGrid cells are editable by default when the grid and column allow editing. Users can double-click a cell or use the keyboard to edit in place. To make a field read-only, set isReadOnly on that column:
<wj-flex-grid-column header="ID" binding="id" [width]="60" [isReadOnly]="true"></wj-flex-grid-column>

Sorting and Column Reordering with FlexGrid
FlexGrid allows users to sort by clicking column headers. When a column should not be sortable, set allowSorting to false on that column:
<wj-flex-grid-column header="Client" binding="client" [width]="150" [allowSorting]="false"></wj-flex-grid-column>
Column dragging is also enabled for FlexGrid columns by default. Disable dragging for an individual column with allowDragging:
<wj-flex-grid-column header="Client" binding="client" [width]="150" [allowDragging]="false"></wj-flex-grid-column>

Import Excel Data into FlexGrid
The import button in the template calls load(importFile).

The component then passes the selected file to FlexGridXlsxConverter.loadAsync.

This keeps file access in the template and removes the need for document.getElementById. The async converter API requires JSZip 3.0, which is why jszip was installed with the Excel package.

Export FlexGrid Data to Excel
The save() method calls FlexGridXlsxConverter.saveAsync and writes the grid data to FlexGrid.xlsx. Include headers for a spreadsheet users can open immediately, and disable cell styles when you want a smaller, data-focused export.

You can now open the Excel file exported from the FlexGrid component.

Ready to try it out? Download Wijmo Today!
Conclusion
There are many ways to add smart tables to Angular applications. Open-source projects can be a solid fit for lightweight scenarios, especially when your team is comfortable writing and maintaining the extra templates and behaviors around them.
However, for enterprise applications, the question is less “Can we make this work?” and more “How much grid code do we want to own?” Wijmo FlexGrid provides a compact, Angular data grid with built-in editing, sorting, virtualization, column operations, Excel import/export, templates, and more. FlexGrid lets developers spend less time rebuilding table infrastructure and more time shipping the business features around the data. Download the complete sample to try this tutorial out today.