Introduction


NgxPanemuTable is an Angular table component. It is designed to be easy to use. Most work will be in typescript file, while the html file only needs to have a very simple tag.
Features
Declarative table definition in typescript. Very little code in html needed.
Sane defaults that can be overriden app-wide and per table basis.
Pagination. More flexible than common pagination.
- Previous page of first page overflow to last page and vice versa.
- User can input arbitrary range.
- Client or server side. Client side pagination is provided, server side implementation is up to you.
- Developer can create custom pagination component. See
Custom Pagination .
Filtering.
- Query builder supports nested AND/OR predicates.
- Provided ready to use client-side sorting, filtering, pagination and grouping
- Filter behavior can be customized. See
Global Search .
Row Grouping.
- Support group level pagination.
- Customizable row group header and footer. See
Custom Row Group .
Column header
- Group multiple columns under one cell header. See
Grouped Column . - Support custom cell header renderer. You can put anything in column header. See
Custom Column Header .
- Group multiple columns under one cell header. See
Column Resizable.
User can change column visibility, position and stickiness at runtime. See
.PanemuSettingComponent Save table states:
- Columns visibility, position and stickiness.
- Filter, grouping, sorting and pagination.
If user changes any of those, go to other and back, the states are restored. See
Persist States .Customizable table cell.
- Support custom cell using
ng-templateor angular component. For simple example seeCustom Column . For more advanced, seeCell Renderer . - Support
custom cell formatting andstyling . Cell Expansion .
- Support custom cell using
Sticky column, header and footer. The footer usage example can be seen in
Virtual Scroll .Cell colspan and rowspan. See
Cell Spanning .Export to CSV. See
.PanemuTableController.getCsvData() Handle huge data using
virtual scroll . Now it doesn't support variable row height. But it will in the future.
This is a quick demo of , , and .
import { Component, inject, OnInit, signal, TemplateRef, viewChild } from '@angular/core';
import { ComputedColumn , DefaultCellRenderer , DefaultRowGroupRenderer , PanemuPaginationComponent , PanemuQueryComponent , PanemuSettingComponent , PanemuTableComponent , PanemuTableController , PanemuTableDataSource , PanemuTableService , Predicate , TickColumnController } from 'ngx-panemu-table';
import { People } from '../model/people';
import { DataService } from '../service/data.service';
import { FilterCountryCellComponent } from './custom-cell/filter-country-cell.component';
import { PeopleFormComponent } from './custom-cell/people-form.component';
@Component({
templateUrl: 'all-features-client.component.html',
imports: [PanemuTableComponent , PanemuPaginationComponent , PanemuQueryComponent , PanemuSettingComponent ],
styleUrl: 'all-features-client.component.scss'
})
export class AllFeaturesClientComponent implements OnInit {
genderMap = signal({});
sendEmailTemplate = viewChild<TemplateRef<any>>('sendEmailTemplate');
actionCellTemplate = viewChild<TemplateRef<any>>('actionCellTemplate');
countryFooter = viewChild<TemplateRef<any>>('countryFooter');
clmAction: ComputedColumn <People> = {
type: 'computed',
formatter: (val: any) => '',
cellRenderer: DefaultCellRenderer .create(this.actionCellTemplate),
expansion: { component: PeopleFormComponent },
sticky: 'end',
width: 80,
resizable: false,
cellStyle: (_: string) => 'border-left-color: rgba(0,0,0, .12); border-left-width: 1px; border-left-style: solid;'
};
pts = inject(PanemuTableService );
dataService = inject(DataService);
columns = this.pts.buildColumns<People>([
{ type:'tick', width: 50, controller: new TickColumnController () },
{ field: 'id', type: 'int', width: 60},
{ field: 'name', width: 150 },
{
field: 'email', width: 230, expansion: {
component: this.sendEmailTemplate,
isDisabled: (row) => {
return row.country == 'ID'
},
}
},
{ field: 'gender', width: 80, type: 'map', valueMap: this.genderMap },
{
field: 'country', width: 150, type: 'map', valueMap: this.dataService.getCountryMap(),
cellRenderer: FilterCountryCellComponent.create(this.onCountryFilterClick.bind(this)),
rowGroupRenderer: DefaultRowGroupRenderer .create({ footerComponent: this.countryFooter })
},
{ field: 'amount', width: 100, type: 'decimal' },
{
type: 'group', label: 'Date Info', children: [
{ field: 'enrolled', width: 150, type: 'date' },
{ field: 'last_login', width: 180, type: 'datetime' },
]
},
{ field: 'verified', width: 80, type: 'boolean' },
this.clmAction,
]
)
datasource = new PanemuTableDataSource <People>;
controller = PanemuTableController .create<People>(this.columns, this.datasource);
ngOnInit() {
//set initial grouping
this.controller.groupByColumns = [{ field: 'country' }]
//set inital filtering
this.controller.criteria = { field: 'verified', value: true, type: 'eq' } as Predicate ;
this.dataService.getPeople().subscribe(result => {
this.datasource.setData(result);
this.controller.reloadData();
});
//pretend genderMap is taken from server data
setTimeout(() => {
this.genderMap.set({ F: 'Female', M: 'Male' })
}, 1000);
}
onCountryFilterClick(value: any, propName: string) {
this.controller.criteria = { field: propName, value: value, type: 'eq' } as Predicate ;
this.controller.reloadData();
}
edit(row: People) {
this.controller.expand(row, this.clmAction)
}
delete(row: People) {
alert('Delete ' + JSON.stringify(row))
}
}More In The Future
These features are not developed yet. Please create a ticket in
- Virtual scroll with variable row height.
Releases:
v.0.7.0
- [New] Query builder component supports AND/OR nested predicates.
- [Breaking Change] Tick column API overhaul.
TickColumnClassis removed. Create tick columns with{ type: 'tick', controller: newand read the selection viaTickColumnController <T>() }controller.tickedRowsSignal(now a getter property, not the oldgetTickedRowsAsSignal()method). SeeTick Column . - [Breaking Change]
is now generic —CellFormatter — so theCellFormatter <T>rowDataandcolumnparameters are typed. - [Breaking Change]
andComputedColumn are now generic (GroupedColumn ,ComputedColumn <T>). The internalGroupedColumn <T>NonGroupColumn<T>union type was removed. - [Breaking Change] New
interface.LeafColumn <T>andCellComponent <T>now declareHeaderComponent column:(wasLeafColumn <T>). Custom cell/header renderers should update theirPropertyColumn <T>columnfield type toso they also work with computed, tick and map columns.LeafColumn <T> - [Breaking Change]
is now required (was optional).MapColumn.type : 'map' - [Breaking Change] Row grouping now keeps the original data type of the grouped value.
is no longer always coerced toRowGroupData.value string. If you were comparingrowGroup.data.value === 'someString'against a boolean or numeric field, update the comparison.
v.0.6.6
- Support Angular 21.
v.0.6.4
- Multi-column sorting with Ctrl key.
- Escape special characters in search highlight regex.
v.0.6.0
- Support Angular 20
v.0.5.0
- Support row double click action. See
Basic Usage . - Support local error handler. See
Error Handling .
v.0.4.0
- [Breaking Change] Persist state now has a way to pick what states to save rather than always saving all states.
See this page . - Icons can be changed. See
Icons
v.0.3.0
- Inline editing
v.0.2.0
- Transpose selected row.
v.0.0.9
- New
as the UI to change columns visibility, position and stickiness.PanemuSettingComponent - Save table states (column structure, pagination, filtering, sorting and grouping)
- Support cell rowspan and colspan using
.RowRenderer
v.0.0.7
- Virtual scroll
- Table footer
- RowGroup now customizable and can have footer
Support Us
About Panemu
We are software development company.