npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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 lazyLoading is 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 true to include the row or false to 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 lazyLoading is false). 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 loadData events instead of handling data locally.
  • Required with: dataLength input 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: any or any[]
  • 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 autoSaveOnChange is true).
  • Note: Columns must have allowEditColumn: true to 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 fieldValueChanged event 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 allowSelectRow and allowExtendRow are 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 showCreateButton is 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: extendedRowTemplate must 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' or width: '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 name
  • tooltipName (string, optional): Tooltip text
  • iconColor ('primary' | 'warn' | 'accent', optional): Icon color
  • action ((row: any, index?: number) => void, optional): Function called when clicked
  • show ((row: any) => boolean, optional): Function to conditionally show/hide the action
  • url (string[], optional): Router navigation path (alternative to action)

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 autoSaveOnChange is 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

  1. Use localStorageName to persist user preferences (column order, filters, sorting)

  2. Enable lazyLoading for large datasets (1000+ records)

  3. Use autoSaveOnChange for simple quantity/selection tables where immediate updates are desired

  4. Use setNewDataWithoutRefresh when frequently updating data to prevent UI flickering

  5. Provide validators (canEditRowValidator, customEditRowValidator) to enforce business rules

  6. Use custom templates for complex cell rendering instead of trying to format with customValueDisplay

  7. Set appropriate column widths to prevent layout shifts

  8. Use notShowInColumnPick for action columns or columns that should always be visible

  9. Implement proper error handling in your event handlers (saveEditedRow, rowDeleted, etc.)

  10. Use selectRowValueDisplay when 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 setNewDataWithoutRefresh if you want in-place updates

Focus lost during editing

  • Enable setNewDataWithoutRefresh for frequent updates
  • Ensure data array length doesn't change during edits

Filters not working

  • Check that columns have filter: true
  • Verify filterType is set correctly
  • For custom filters, provide customFilterOptions

Lazy loading not triggered

  • Ensure lazyLoading is true
  • Check that loadData event handler is connected
  • Verify you're setting dataLength with total record count

Row editing disabled

  • Check allowEditRow is true
  • Verify columns have allowEditColumn: true
  • Check canEditRowValidator isn'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.