Skip to content

dirkluijk/ngx-generic-material-tables

Repository files navigation

Generic Angular Material Tables 🚀

Sorting and filtering utils to create generic Angular Material tables

NPM version NPM downloads Build status All Contributors

Overview

What? 🤔

A small set of utils to make Angular Material Tables more generic.

  • Sorting based on column names, with nested property support using dot notation
  • Filtering based on column names, with nested property support using dot notation
  • Sorts string values case-insensitive
  • Filter only on displayed columns (case-insensitive and trimming the filter string)
  • Persisted sorting using SessionStorage
  • Reactive data source for RxJS Observables
  • Reloading functionality (for reactive data sources)

Why? 🤷‍♂️

When using Angular Material Table, you may need more advanced sorting and filtering behaviour. That's why you usually end up with a lot of boilerplate code.

This library provides some utils to use consistent sorting and filtering behaviour for all your tables.

Installation 🌩

npm
npm install @dirkluijk/ngx-generic-material-tables --save-dev
yarn
yarn add @dirkluijk/ngx-generic-material-tables --dev

Usage 🕹

GenericTableDataSource

The GenericTableDataSource allows you to use "dot notation" for your columns and benefit from the following features:

  • Only filter on the displayed columns (which you need to pass to it), using the dot notation
  • Use the sortable columns based on the values accessed using the dot notation
import { Component } from '@angular/core';
import { GenericTableDataSource } from '@dirkluijk/ngx-generic-material-tables'

@Component({
    template: `
      <table mat-table [dataSource]="genericDataSource" matSort gmtApplyMatSort>
        <ng-container matColumnDef="name">
          <th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
          <td mat-cell *matCellDef="let row">
              {{ row.name }}
          </td>
        </ng-container>

        <ng-container matColumnDef="foo.bar">
          <th mat-header-cell *matHeaderCellDef mat-sort-header>Foo Bar</th>
          <td mat-cell *matCellDef="let row">
            {{ row.foo.bar }}
          </td>>
        </ng-container>
          
        <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
        <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
    </table>
    `
})
export class MyComponent {
    public displayedColumns = ['name', 'foo.bar'];
    public genericDataSource = new GenericTableDataSource<YourRecordEntity>(this.displayedColumns, [/** your data */]);
}

Apply MatSort automatically

Use the gmtApplyMatSort to automatically register the MatSort on the data source.

<table mat-table [dataSource]="genericDataSource" matSort gmtApplyMatSort>
  <!-- ...  -->
</table>

This allows you to leave out the following code:

ViewChild(MatSort, {static: true}) sort: MatSort; // not needed anymore!

ngOnInit(): void {
  this.dataSource.sort = this.sort; // not needed anymore!
}

Persisted sorting

You can persist the sorting actions to SessionStorage using the gmtPersistedSort directive. Just pass an additional identifier for your table in order to distinguish between multiple tables.

<table mat-table [dataSource]="genericDataSource" matSort gmtPersistedSort="my-table">
  <!-- ...  -->
</table>

That's it!

ReactiveGenericTableDataSource

Even more awesome is the reactive version of the GenericTableDataSource:

  • Pass the displayed columns, table data and filter data as Observable stream
  • Automatically reacts to changes
  • Provides reload() functionality
  • Provides loading/success/failed static properties
  • Provides loading$/success$/failed$ Observable properties
import { ReactiveGenericTableDataSource } from '@dirkluijk/ngx-generic-material-tables'

const dataSource = new ReactiveGenericTableDataSource<YourRecordEntity>(
  displayedColumns$,
  yourTableData$,
  yourFilter$ // (optional)
);

dataSource.reload();

Custom filtering for specific columns / types

If you still want to have custom filtering for a specific column, filter value or column value, you can pass a ColumnFilterPredicate:

import { defaultColumnFilterPredicate } from '@dirkluijk/ngx-generic-material-tables';

dataSource.columnFilterPredicate = (value, filter, columnName) => {
   // exact filter for this column
  if (columnName === 'vehicle.number') {
    return value.trim().includes(filter);
  }

  // custom filter for number values
  if (typeof value === 'number') {
    return String(yourFormatFn(value)).includes(filter);
  }

  return defaultColumnFilterPredicate(value, filter, columnName); // use default predicate for other cases
};

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Dirk Luijk
Dirk Luijk

💻 📖

This project follows the all-contributors specification. Contributions of any kind welcome!