ngx-st-tables
v18.0.32
Published
- [Overview](#overview) - [Basic Inputs](#basic-inputs) - [Display & Layout Inputs](#display--layout-inputs) - [Data Management Inputs](#data-management-inputs) - [Row Selection Inputs](#row-selection-inputs) - [Row Editing Inputs](#row-editing-input
Readme
Material Table Component - Complete Input/Output Reference
Table of Contents
- Overview
- Basic Inputs
- Display & Layout Inputs
- Data Management Inputs
- Row Selection Inputs
- Row Editing Inputs
- Row Actions Inputs
- Column Configuration
- Outputs (Events)
- Usage Examples
Overview
The ngx-st-material-table component is a powerful Angular Material table with features including:
- Pagination, sorting, and filtering
- Lazy loading support
- Row editing (inline editing or auto-save)
- Row selection (single or multiple)
- Custom column templates
- Expandable rows
- Local storage persistence
- Create, update, and delete operations
Basic Inputs
tableTitle
- Type:
string - Default:
'' - Description: Title displayed at the top of the table in the caption area.
- Example:
tableTitle="Users List"
pageSize
- Type:
number - Default:
10 - Description: Number of rows to display per page.
- Example:
[pageSize]="25"
dataLength
- Type:
number - Default:
0 - Description: Total number of records (used for lazy loading pagination). Only needed when
lazyLoadingis true. - Example:
[dataLength]="totalRecords"
data
- Type:
any[] - Default:
[] - Description: Array of data objects to display in the table. Each object represents one row.
- Important: Uses immutable data pattern. Always pass a new array reference when updating data.
- Example:
[data]="users"
initColumns
- Type:
StMaterialTableColumnModel[] - Default:
[] - Description: Array of column definitions that configure how each column is displayed and behaves.
- See: Column Configuration section for details.
- Example:
[initColumns]="columnDefs"
Display & Layout Inputs
showGlobalSearch
- Type:
boolean - Default:
false - Description: Shows a global search input that searches across all visible columns.
- Example:
[showGlobalSearch]="true"
allowPickColumns
- Type:
boolean - Default:
false - Description: Allows users to show/hide columns via a column picker dialog.
- Example:
[allowPickColumns]="true"
allowReorderColumns
- Type:
boolean - Default:
false - Description: Allows users to reorder columns by dragging them.
- Example:
[allowReorderColumns]="true"
localStorageName
- Type:
string - Default:
'' - Description: Key name for storing table state (pagination, sorting, filters, column order) in localStorage. If empty, state is not persisted.
- Example:
localStorageName="users-table-state"
isLoading
- Type:
boolean - Default:
false - Description: Shows a loading spinner overlay on the table.
- Example:
[isLoading]="loading"
extraCustomFilter
- Type:
TemplateRef<any> - Default:
undefined - Description: Custom template for additional filter controls displayed in the table caption area.
- Example:
<ng-template #customFilters> <mat-form-field> <mat-select placeholder="Status"> <mat-option value="active">Active</mat-option> <mat-option value="inactive">Inactive</mat-option> </mat-select> </mat-form-field> </ng-template> [extraCustomFilter]="customFilters"
initFilterMethod
- Type:
(row: any) => boolean - Default:
undefined - Description: Custom filter function that is applied to every row in the table. The function receives a row object and should return
trueto include the row orfalseto exclude it. This filter is automatically applied whenever table data changes and works in combination with other filters (global search, column filters). - Use Cases:
- Filter rows based on complex business logic
- Show only rows matching specific conditions
- Dynamically filter based on external state changes
- Note: Only works with local data (when
lazyLoadingisfalse). For server-side filtering, handle filtering in your backend. - Examples:
// Show only active users [initFilterMethod]="(row) => row.status === 'active'" // Show users with quantity greater than 10 [initFilterMethod]="(row) => row.quantity > 10" // Complex filtering with multiple conditions [initFilterMethod]="filterRows.bind(this)" filterRows(row: any): boolean { return row.isActive && row.amount > 0 && row.department === this.selectedDepartment; } // Dynamic filtering based on date range [initFilterMethod]="(row) => { const rowDate = new Date(row.createdAt); return rowDate >= this.startDate && rowDate <= this.endDate; }"
Data Management Inputs
lazyLoading
- Type:
boolean - Default:
false - Description: Enables server-side pagination, sorting, and filtering. When true, table emits
loadDataevents instead of handling data locally. - Required with:
dataLengthinput must be provided - Example:
[lazyLoading]="true" [dataLength]="totalRecords" (loadData)="onLoadData($event)"
setNewDataWithoutRefresh
- Type:
boolean - Default:
false - Description: When true and data array length hasn't changed, updates existing row objects in place without recreating table rows. This preserves focus state during editing and prevents UI flickering.
- Use case: Ideal for real-time updates or auto-save scenarios where you frequently update data.
- Example:
[setNewDataWithoutRefresh]="true"
Row Selection Inputs
allowSelectRow
- Type:
boolean - Default:
false - Description: Enables row selection with checkboxes. Adds a selection column to the left of the table.
- Example:
[allowSelectRow]="true" (selectRowChange)="onSelectionChange($event)"
selectionFieldLabel
- Type:
string - Default:
'' - Description: Property name from the row object to use as the display label for selected rows (shown in chips).
- Example:
selectionFieldLabel="name"
selectRowOnlyOne
- Type:
boolean - Default:
false - Description: When true, only one row can be selected at a time (radio button behavior instead of checkboxes).
- Example:
[selectRowOnlyOne]="true"
selectRowValueDisplay
- Type:
(row: any) => string - Default:
undefined - Description: Custom formatter function to generate the display value for selected rows. Takes precedence over
selectionFieldLabel. - Use case: When you need to display multiple fields or apply custom formatting.
- Example:
[selectRowValueDisplay]="formatSelectedRow" formatSelectedRow(row: any): string { return `${row.firstName} ${row.lastName} (${row.email})`; }
initSelectedRow
- Type:
anyorany[] - Default:
undefined - Description: Pre-selected row(s) when the table initializes.
- Example:
[initSelectedRow]="preSelectedUsers"
Row Editing Inputs
allowEditRow
- Type:
boolean - Default:
false - Description: Enables inline row editing. Adds edit/delete action buttons to each row (unless
autoSaveOnChangeis true). - Note: Columns must have
allowEditColumn: trueto be editable. - Example:
[allowEditRow]="true" (saveEditedRow)="onRowSaved($event)"
allowEditInEditRow
- Type:
boolean - Default:
true - Description: Shows the edit button in the actions column. Set to false to hide it while still allowing editing functionality.
- Example:
[allowEditInEditRow]="false"
allowDeleteInEditRow
- Type:
boolean - Default:
true - Description: Shows the delete button in the actions column.
- Example:
[allowDeleteInEditRow]="false"
showEditAllRows
- Type:
boolean - Default:
false - Description: Shows a button in the table caption that toggles edit mode for all rows simultaneously.
- Example:
[showEditAllRows]="true"
autoSaveOnChange
- Type:
boolean - Default:
false - Description: When true, field changes are saved immediately without requiring save/cancel buttons. Perfect for quantity inputs, toggles, or selection tables where instant updates are needed.
- Behavior:
- Removes save/cancel buttons from the table
- Emits
fieldValueChangedevent on every field change - Parent should update their data source immediately
- Example:
[autoSaveOnChange]="true" (fieldValueChanged)="onFieldChanged($event)" onFieldChanged(event: { row: any; field: string; value: any; index: number }) { // Update your data source this.users[event.index][event.field] = event.value; // Optionally call API to persist this.updateUser(event.row); }
canEditRowValidator
- Type:
(row: any) => boolean - Default:
() => true - Description: Function to determine if a specific row can be edited. Return false to disable editing for that row.
- Example:
[canEditRowValidator]="canEditUser" canEditUser(row: any): boolean { return row.status !== 'archived' && row.isEditable; }
canDeleteRowValidator
- Type:
(row: any) => boolean - Default:
() => true - Description: Function to determine if a specific row can be deleted. Return false to disable delete button for that row.
- Example:
[canDeleteRowValidator]="canDeleteUser" canDeleteUser(row: any): boolean { return row.status !== 'system' && !row.hasRelatedRecords; }
Row Actions Inputs
rowClickAction
- Type:
(row: any) => void - Default:
undefined - Description: Function called when a row is clicked (only if
allowSelectRowandallowExtendRoware false). - Example:
[rowClickAction]="onRowClick" onRowClick(row: any): void { this.router.navigate(['/users', row.id]); }
allowCreateRow
- Type:
boolean - Default:
false - Description: Shows an "Add New Row" button below the table that adds a new editable row.
- Example:
[allowCreateRow]="true" (saveCreatedRow)="onRowCreated($event)"
showCreateButton
- Type:
boolean - Default:
false - Description: Shows a custom create button in the table caption (separate from
allowCreateRow). - Example:
[showCreateButton]="true" [createButtonLabel]="'Add User'" [createButtonAction]="openCreateDialog"
createButtonLabel
- Type:
string - Default:
'Create' - Description: Label text for the create button (when
showCreateButtonis true). - Example:
createButtonLabel="Add New User"
createButtonAction
- Type:
() => void - Default:
() => {} - Description: Function called when the create button is clicked.
- Example:
[createButtonAction]="openCreateDialog" openCreateDialog(): void { // Open a dialog or navigate to create page }
disableCreateButton
- Type:
boolean - Default:
false - Description: Disables the create button.
- Example:
[disableCreateButton]="!hasPermission"
Row Expansion Inputs
allowExtendRow
- Type:
boolean - Default:
false - Description: Enables expandable rows with a toggle button. Clicking a row expands/collapses its detail view.
- Required with:
extendedRowTemplatemust be provided. - Example:
[allowExtendRow]="true" [extendedRowTemplate]="detailTemplate"
extendedRowTemplate
- Type:
TemplateRef<{ data: any }> - Default:
undefined - Description: Template for the expanded row content. Receives the row data via context.
- Example:
<ng-template #detailTemplate let-data="data"> <div class="row-details"> <h3>User Details</h3> <p><strong>Email:</strong> {{ data.email }}</p> <p><strong>Phone:</strong> {{ data.phone }}</p> <p><strong>Address:</strong> {{ data.address }}</p> </div> </ng-template> [extendedRowTemplate]="detailTemplate"
Column Configuration
Columns are configured via the initColumns input using the StMaterialTableColumnModel interface.
Column Model Properties
Basic Properties
field
- Type:
string - Required: Yes
- Description: Property name from the data object to display in this column.
- Example:
field: 'firstName'
header
- Type:
string - Required: Yes
- Description: Column header text displayed in the table header.
- Example:
header: 'First Name'
type
- Type:
StMaterialColumnType - Options:
'string'|'number'|'boolean'|'date'|'custom-template'|'actions' - Default:
'string' - Description: Data type of the column, affects display formatting and default filter/edit types.
- Example:
type: 'date'
width
- Type:
string - Default: Auto
- Description: CSS width value for the column.
- Example:
width: '150px'orwidth: '20%'
flexRight
- Type:
boolean - Default:
false - Description: Aligns column content to the right.
- Example:
flexRight: true
Sorting & Filtering
sort
- Type:
boolean - Default:
true - Description: Enables sorting for this column.
- Example:
sort: false
filter
- Type:
boolean - Default:
true - Description: Enables filtering for this column.
- Example:
filter: true
filterType
- Type:
StMaterialColumnFilterType - Options:
'string'|'number'|'boolean'|'date'|'custom' - Default: Auto-detected from
type - Description: Type of filter input to show for this column.
- Example:
filterType: 'date'
customFilterOptions
- Type:
{ value: string; label: string }[] - Default:
undefined - Description: Options for a dropdown filter (when filterType is 'custom').
- Example:
customFilterOptions: [ { value: 'active', label: 'Active' }, { value: 'inactive', label: 'Inactive' } ]
Display Customization
customTemplate
- Type:
TemplateRef<any> - Default:
undefined - Description: Custom template for rendering cell content (use with
type: 'custom-template'). - Example:
<ng-template #statusTemplate let-row="row"> <span [class]="'status-' + row.status">{{ row.status }}</span> </ng-template> { field: 'status', header: 'Status', type: 'custom-template', customTemplate: statusTemplate }
customValueDisplay
- Type:
(row: any) => string - Default:
undefined - Description: Function to transform the cell value for display.
- Example:
{ field: 'price', header: 'Price', customValueDisplay: (row) => `$${row.price.toFixed(2)}` }
translateValue
- Type:
{ [value: string]: string } - Default:
undefined - Description: Map of values to translated/display values.
- Example:
{ field: 'status', header: 'Status', translateValue: { 'A': 'Active', 'I': 'Inactive', 'P': 'Pending' } }
Row Editing Configuration
allowEditColumn
- Type:
boolean - Default:
false - Description: Makes this column editable when row is in edit mode.
- Example:
allowEditColumn: true
rowEditType
- Type:
StMaterialRowEditType - Options:
'string'|'number'|'boolean'|'date'|'custom'|'custom-dynamic-select'|'number-qty-input' - Default: Auto-detected from
type - Description: Type of input to show when editing this column.
'string': Text input field'number': Standard HTML number input'boolean': Toggle/checkbox control'date': Date picker'custom': Dropdown with static options'custom-dynamic-select': Dropdown with row-dependent options'number-qty-input': Quantity input component with increment/decrement buttons
- Example:
rowEditType: 'number' - Qty Input Example:
{ field: 'quantity', header: 'Quantity', type: 'number', allowEditColumn: true, rowEditType: 'number-qty-input', customEditRowValidator: (oldValue, newValue, row) => { if (newValue < 0) { return { isValid: false, errorMessage: 'Quantity cannot be negative' }; } return { isValid: true, errorMessage: '' }; } }
customRowEditOptions
- Type:
{ value: any; label: string }[] - Default:
undefined - Description: Static options for a dropdown editor (when rowEditType is 'custom').
- Example:
customRowEditOptions: [ { value: 'admin', label: 'Administrator' }, { value: 'user', label: 'Regular User' }, { value: 'guest', label: 'Guest' } ]
dynamicRowEditOptions
- Type:
(row: any) => { value: any; label: string }[] - Default:
undefined - Description: Dynamic function to generate dropdown options based on the current row (when rowEditType is 'custom-dynamic-select').
- Use case: When options depend on other field values in the row.
- Example:
dynamicRowEditOptions: (row) => { if (row.country === 'USA') { return [ { value: 'NY', label: 'New York' }, { value: 'CA', label: 'California' } ]; } else { return [ { value: 'LON', label: 'London' }, { value: 'MAN', label: 'Manchester' } ]; } }
editColumnRequired
- Type:
boolean - Default:
false - Description: Makes this column required when editing. Shows error if empty on save.
- Example:
editColumnRequired: true
disableEdit
- Type:
(row: any) => boolean - Default:
undefined - Description: Function to determine if this specific column should be disabled for editing in a particular row.
- Example:
disableEdit: (row) => row.status === 'locked'
customEditRowValidator
- Type:
(oldValue: any, newValue: any, row: any) => { isValid: boolean; errorMessage: string } - Default:
undefined - Description: Custom validation function for this column during editing.
- Example:
customEditRowValidator: (oldValue, newValue, row) => { if (newValue < 0) { return { isValid: false, errorMessage: 'Value cannot be negative' }; } if (newValue > row.maxValue) { return { isValid: false, errorMessage: 'Value exceeds maximum' }; } return { isValid: true, errorMessage: '' }; }
Actions Column Configuration
actions
- Type:
StMaterialTableActionColumnModel[] - Default:
undefined - Description: Array of action buttons to show in this column (use with
type: 'actions'). - Example:
{ field: 'actions', header: 'Actions', type: 'actions', sort: false, filter: false, actions: [ { iconName: 'edit', tooltipName: 'Edit User', iconColor: 'primary', action: (row) => this.editUser(row), show: (row) => row.canEdit }, { iconName: 'delete', tooltipName: 'Delete User', iconColor: 'warn', action: (row) => this.deleteUser(row), show: (row) => row.canDelete } ] }
actionsInMenu
- Type:
boolean - Default:
false - Description: Shows actions in a dropdown menu instead of as individual buttons.
- Example:
actionsInMenu: true
Action Button Model
Each action in the actions array has the following properties:
iconName(string, required): Material icon nametooltipName(string, optional): Tooltip texticonColor('primary' | 'warn' | 'accent', optional): Icon coloraction((row: any, index?: number) => void, optional): Function called when clickedshow((row: any) => boolean, optional): Function to conditionally show/hide the actionurl(string[], optional): Router navigation path (alternative toaction)
Other Properties
notShowInColumnPick
- Type:
boolean - Default:
false - Description: Hides this column from the column picker dialog.
- Example:
notShowInColumnPick: true
selectColumnLabel
- Type:
string - Default:
undefined - Description: Used for special columns like selection, editing, extending. Sets the aria-label.
Outputs (Events)
loadData
- Type:
StMaterialTableLoadData - When emitted: When lazy loading is enabled and data needs to be loaded (pagination, sorting, filtering changes).
- Payload:
{ first: number; // Starting index rows: number; // Number of rows to load globalFilter: string; // Global search text globalFilterColumns: string[]; // Columns to search sortField: string; // Field to sort by sortOrder: SortDirection; // 'asc' | 'desc' filters: { [key: string]: StMaterialTableFilter }; // Column filters } - Example:
(loadData)="onLoadData($event)" onLoadData(event: StMaterialTableLoadData): void { this.loading = true; this.userService.getUsers(event).subscribe(response => { this.users = response.data; this.totalRecords = response.total; this.loading = false; }); }
saveEditedRow
- Type:
{ row: any; index: number } - When emitted: When user saves an edited existing row.
- Payload: The modified row object and its index.
- Example:
(saveEditedRow)="onRowEdited($event)" onRowEdited(event: { row: any; index: number }): void { this.userService.updateUser(event.row).subscribe( () => { // Update your data array this.users[event.index] = event.row; this.snackbar.success('User updated successfully'); }, error => { this.snackbar.error('Failed to update user'); } ); }
saveCreatedRow
- Type:
any - When emitted: When user saves a newly created row (via
allowCreateRow). - Payload: The new row object.
- Example:
(saveCreatedRow)="onRowCreated($event)" onRowCreated(row: any): void { this.userService.createUser(row).subscribe( (createdUser) => { // Add to your data array this.users = [...this.users, createdUser]; this.snackbar.success('User created successfully'); }, error => { this.snackbar.error('Failed to create user'); } ); }
rowDeleted
- Type:
{ row: any; index: number } - When emitted: When user deletes a row.
- Payload: The deleted row object and its index.
- Example:
(rowDeleted)="onRowDeleted($event)" onRowDeleted(event: { row: any; index: number }): void { this.userService.deleteUser(event.row.id).subscribe( () => { // Remove from your data array this.users = this.users.filter(u => u.id !== event.row.id); this.snackbar.success('User deleted successfully'); }, error => { this.snackbar.error('Failed to delete user'); } ); }
selectRowChange
- Type:
any[] - When emitted: When row selection changes.
- Payload: Array of selected row objects.
- Example:
(selectRowChange)="onSelectionChange($event)" onSelectionChange(selectedRows: any[]): void { this.selectedUsers = selectedRows; console.log(`${selectedRows.length} users selected`); }
fieldValueChanged
- Type:
{ row: any; field: string; value: any; index: number } - When emitted: When
autoSaveOnChangeis enabled and a field value changes. - Payload: The row, field name, new value, and row index.
- Important: You must update your data source immediately when receiving this event.
- Example:
(fieldValueChanged)="onFieldChanged($event)" onFieldChanged(event: { row: any; field: string; value: any; index: number }): void { // Update local data this.users[event.index][event.field] = event.value; // Optionally persist to server this.userService.updateUser(event.row).subscribe(); }
Usage Examples
Example 1: Basic Table with Sorting and Filtering
// Component
columns: StMaterialTableColumnModel[] = [
{ field: 'id', header: 'ID', width: '80px' },
{ field: 'firstName', header: 'First Name', filter: true },
{ field: 'lastName', header: 'Last Name', filter: true },
{ field: 'email', header: 'Email', filter: true },
{ field: 'age', header: 'Age', type: 'number' }
];
users: any[] = [
{ id: 1, firstName: 'John', lastName: 'Doe', email: '[email protected]', age: 30 },
{ id: 2, firstName: 'Jane', lastName: 'Smith', email: '[email protected]', age: 25 }
];<!-- Template -->
<ngx-st-material-table
tableTitle="Users"
[pageSize]="10"
[data]="users"
[initColumns]="columns"
[showGlobalSearch]="true"
[allowPickColumns]="true"
localStorageName="users-table">
</ngx-st-material-table>Example 2: Lazy Loading Table
// Component
columns: StMaterialTableColumnModel[] = [
{ field: 'name', header: 'Name', filter: true },
{ field: 'status', header: 'Status', filter: true, filterType: 'custom',
customFilterOptions: [
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' }
]
}
];
users: any[] = [];
totalRecords = 0;
loading = false;
onLoadData(event: StMaterialTableLoadData): void {
this.loading = true;
this.userService.getUsers(event).subscribe(response => {
this.users = response.data;
this.totalRecords = response.total;
this.loading = false;
});
}<!-- Template -->
<ngx-st-material-table
[lazyLoading]="true"
[isLoading]="loading"
[dataLength]="totalRecords"
[data]="users"
[initColumns]="columns"
(loadData)="onLoadData($event)">
</ngx-st-material-table>Example 3: Editable Table with Validation
// Component
columns: StMaterialTableColumnModel[] = [
{
field: 'name',
header: 'Name',
allowEditColumn: true,
editColumnRequired: true
},
{
field: 'quantity',
header: 'Quantity',
type: 'number',
allowEditColumn: true,
rowEditType: 'number',
editColumnRequired: true,
customEditRowValidator: (oldValue, newValue, row) => {
if (newValue < 0) {
return { isValid: false, errorMessage: 'Quantity cannot be negative' };
}
if (newValue > 100) {
return { isValid: false, errorMessage: 'Quantity cannot exceed 100' };
}
return { isValid: true, errorMessage: '' };
}
},
{
field: 'status',
header: 'Status',
allowEditColumn: true,
rowEditType: 'custom',
customRowEditOptions: [
{ value: 'draft', label: 'Draft' },
{ value: 'published', label: 'Published' }
]
}
];
onRowEdited(event: { row: any; index: number }): void {
this.updateItem(event.row);
}
canEditItem(row: any): boolean {
return row.status !== 'archived';
}<!-- Template -->
<ngx-st-material-table
[data]="items"
[initColumns]="columns"
[allowEditRow]="true"
[allowCreateRow]="true"
[canEditRowValidator]="canEditItem"
(saveEditedRow)="onRowEdited($event)"
(saveCreatedRow)="onRowCreated($event)"
(rowDeleted)="onRowDeleted($event)">
</ngx-st-material-table>Example 4: Auto-Save Table (Instant Updates)
// Component
columns: StMaterialTableColumnModel[] = [
{ field: 'product', header: 'Product' },
{
field: 'quantity',
header: 'Quantity',
type: 'number',
allowEditColumn: true,
rowEditType: 'number'
},
{
field: 'active',
header: 'Active',
type: 'boolean',
allowEditColumn: true
}
];
onFieldChanged(event: { row: any; field: string; value: any; index: number }): void {
// Update local data immediately
this.items[event.index][event.field] = event.value;
// Persist to server
this.itemService.updateItem(event.row).subscribe();
}<!-- Template -->
<ngx-st-material-table
[data]="items"
[initColumns]="columns"
[allowEditRow]="true"
[autoSaveOnChange]="true"
[setNewDataWithoutRefresh]="true"
(fieldValueChanged)="onFieldChanged($event)">
</ngx-st-material-table>Example 5: Quantity Input in Table (Auto-Save)
// Component - Using the dedicated qty-input component for quantity columns
columns: StMaterialTableColumnModel[] = [
{ field: 'product', header: 'Product', width: '200px' },
{
field: 'quantity',
header: 'Quantity',
type: 'number',
width: '120px',
allowEditColumn: true,
rowEditType: 'number-qty-input', // Uses qty-input component
customEditRowValidator: (oldValue, newValue, row) => {
if (newValue < 0) {
return { isValid: false, errorMessage: 'Quantity cannot be negative' };
}
if (newValue > row.maxStock) {
return { isValid: false, errorMessage: `Maximum available: ${row.maxStock}` };
}
return { isValid: true, errorMessage: '' };
}
},
{
field: 'price',
header: 'Unit Price',
type: 'number',
customValueDisplay: (row) => `$${row.price.toFixed(2)}`
},
{
field: 'total',
header: 'Total',
type: 'number',
sort: false,
filter: false,
customValueDisplay: (row) => `$${(row.quantity * row.price).toFixed(2)}`
}
];
items = [
{ id: 1, product: 'Widget A', quantity: 5, price: 10.00, maxStock: 100 },
{ id: 2, product: 'Widget B', quantity: 3, price: 15.50, maxStock: 50 },
{ id: 3, product: 'Widget C', quantity: 2, price: 25.00, maxStock: 75 }
];
onFieldChanged(event: { row: any; field: string; value: any; index: number }): void {
// Update local data immediately
this.items[event.index][event.field] = event.value;
// Persist to server if needed
this.itemService.updateItem(event.row).subscribe(
(updatedItem) => {
this.items[event.index] = updatedItem;
},
(error) => {
console.error('Failed to update item', error);
// You might want to rollback the change
}
);
}<!-- Template -->
<ngx-st-material-table
tableTitle="Order Items"
[pageSize]="25"
[data]="items"
[initColumns]="columns"
[allowEditRow]="true"
[autoSaveOnChange]="true"
[setNewDataWithoutRefresh]="true"
[showGlobalSearch]="true"
localStorageName="order-items-table"
(fieldValueChanged)="onFieldChanged($event)">
</ngx-st-material-table>Benefits of using number-qty-input rowEditType:
- Dedicated increment/decrement buttons for easy quantity adjustments
- Cleaner UI compared to standard number inputs (hidden spinner arrows)
- Customizable input width to fit your layout
- Integrated validation support
- Immediate auto-save when used with
autoSaveOnChange - Better user experience for quantity-centric tables
Example 6: Selection Table
// Component
columns: StMaterialTableColumnModel[] = [
{ field: 'id', header: 'ID' },
{ field: 'name', header: 'Name' },
{ field: 'email', header: 'Email' }
];
selectedUsers: any[] = [];
onSelectionChange(selected: any[]): void {
this.selectedUsers = selected;
}
formatSelectedUser(row: any): string {
return `${row.name} (${row.email})`;
}<!-- Template -->
<ngx-st-material-table
[data]="users"
[initColumns]="columns"
[allowSelectRow]="true"
selectionFieldLabel="name"
[selectRowValueDisplay]="formatSelectedUser"
(selectRowChange)="onSelectionChange($event)">
</ngx-st-material-table>Example 7: Expandable Rows
<!-- Template -->
<ng-template #detailTemplate let-data="data">
<div class="row-detail">
<h4>Additional Details</h4>
<p><strong>Address:</strong> {{ data.address }}</p>
<p><strong>Phone:</strong> {{ data.phone }}</p>
<p><strong>Notes:</strong> {{ data.notes }}</p>
</div>
</ng-template>
<ngx-st-material-table
[data]="users"
[initColumns]="columns"
[allowExtendRow]="true"
[extendedRowTemplate]="detailTemplate">
</ngx-st-material-table>Example 8: Custom Column Template
<!-- Template -->
<ng-template #statusBadgeTemplate let-row="row">
<span class="badge" [ngClass]="{
'badge-success': row.status === 'active',
'badge-danger': row.status === 'inactive',
'badge-warning': row.status === 'pending'
}">
{{ row.status | uppercase }}
</span>
</ng-template>// Component
@ViewChild('statusBadgeTemplate') statusBadgeTemplate!: TemplateRef<any>;
ngAfterViewInit() {
this.columns = [
{ field: 'name', header: 'Name' },
{
field: 'status',
header: 'Status',
type: 'custom-template',
customTemplate: this.statusBadgeTemplate
}
];
}Example 9: Actions Column
// Component
columns: StMaterialTableColumnModel[] = [
{ field: 'name', header: 'Name' },
{ field: 'email', header: 'Email' },
{
field: 'actions',
header: 'Actions',
type: 'actions',
sort: false,
filter: false,
width: '120px',
actions: [
{
iconName: 'visibility',
tooltipName: 'View Details',
action: (row) => this.viewUser(row)
},
{
iconName: 'edit',
tooltipName: 'Edit User',
iconColor: 'primary',
action: (row) => this.editUser(row),
show: (row) => row.canEdit
},
{
iconName: 'delete',
tooltipName: 'Delete User',
iconColor: 'warn',
action: (row, index) => this.deleteUser(row, index),
show: (row) => row.canDelete
}
]
}
];
viewUser(row: any): void {
this.router.navigate(['/users', row.id]);
}
editUser(row: any): void {
// Open edit dialog
}
deleteUser(row: any, index: number): void {
// Delete logic
}Example 10: Dynamic Select Options Based on Row
// Component
columns: StMaterialTableColumnModel[] = [
{ field: 'country', header: 'Country', allowEditColumn: true },
{
field: 'state',
header: 'State',
allowEditColumn: true,
rowEditType: 'custom-dynamic-select',
dynamicRowEditOptions: (row) => {
if (row.country === 'USA') {
return [
{ value: 'NY', label: 'New York' },
{ value: 'CA', label: 'California' },
{ value: 'TX', label: 'Texas' }
];
} else if (row.country === 'UK') {
return [
{ value: 'ENG', label: 'England' },
{ value: 'SCT', label: 'Scotland' },
{ value: 'WLS', label: 'Wales' }
];
}
return [];
}
}
];Best Practices
Use
localStorageNameto persist user preferences (column order, filters, sorting)Enable
lazyLoadingfor large datasets (1000+ records)Use
autoSaveOnChangefor simple quantity/selection tables where immediate updates are desiredUse
setNewDataWithoutRefreshwhen frequently updating data to prevent UI flickeringProvide validators (
canEditRowValidator,customEditRowValidator) to enforce business rulesUse custom templates for complex cell rendering instead of trying to format with
customValueDisplaySet appropriate column widths to prevent layout shifts
Use
notShowInColumnPickfor action columns or columns that should always be visibleImplement proper error handling in your event handlers (saveEditedRow, rowDeleted, etc.)
Use
selectRowValueDisplaywhen simple field labels aren't enough for selected row display
Common Patterns
Pattern 1: Master-Detail Table
Use allowExtendRow with extendedRowTemplate to show additional details without navigation.
Pattern 2: Quick Edit Grid
Use autoSaveOnChange with setNewDataWithoutRefresh for spreadsheet-like editing.
Pattern 3: Selection List
Use allowSelectRow with selectRowOnlyOne for choosing a single item (like a lookup).
Pattern 4: CRUD Table
Combine allowEditRow, allowCreateRow, and action handlers for full CRUD operations.
Pattern 5: Filtered Report
Use showGlobalSearch, column filters, and localStorageName for a searchable report with saved preferences.
Pattern 6: Dynamic Custom Filtering
Use initFilterMethod for programmatic filtering based on external state or complex business logic.
export class MyComponent {
minAmount = 100;
selectedDepartment = 'Sales';
filterMethod = (row: any): boolean => {
return row.amount >= this.minAmount &&
row.department === this.selectedDepartment;
};
// When minAmount or selectedDepartment changes,
// update the filter method to trigger refiltering
updateFilters() {
this.filterMethod = (row: any): boolean => {
return row.amount >= this.minAmount &&
row.department === this.selectedDepartment;
};
}
}<ngx-st-material-table
[data]="salesData"
[initColumns]="columns"
[initFilterMethod]="filterMethod">
</ngx-st-material-table>Troubleshooting
Table doesn't update when data changes
- Ensure you're passing a new array reference:
this.data = [...updatedData] - Or use
setNewDataWithoutRefreshif you want in-place updates
Focus lost during editing
- Enable
setNewDataWithoutRefreshfor frequent updates - Ensure data array length doesn't change during edits
Filters not working
- Check that columns have
filter: true - Verify
filterTypeis set correctly - For custom filters, provide
customFilterOptions
Lazy loading not triggered
- Ensure
lazyLoadingis true - Check that
loadDataevent handler is connected - Verify you're setting
dataLengthwith total record count
Row editing disabled
- Check
allowEditRowis true - Verify columns have
allowEditColumn: true - Check
canEditRowValidatorisn't returning false
This documentation covers all inputs, outputs, and configuration options for the Material Table component. For additional help, refer to the component source code or contact the development team.
