Telerik blogs

Angular Material is a popular library created by the Angular team. Let’s build a data grid using mat-table to see how it works.

One of the most common ways to show data in apps is using a list or table. It allows users to list, filter, sort and page through data. Angular Material is a package that gives us a list of components to create a beautiful interface using its components in our apps.

The Angular team created and supports Angular Material. It provides a set of visual components following the Material Design Guidelines that allow us to develop consistent user interfaces.

We’re going to build a data grid with Angular Material. The implementation is divided into six parts and focuses on the mat-table directive for creating a data grid to show data; to allow sorting, filtering and pagination; and to boost performance.

  • Add Angular Material
  • List data with mat-table
  • Add pagination
  • Allow sorting
  • Add filtering
  • Boost performance

Read more about previous topics:

Add Angular Material

Let us create the project first.

ng new datagrid-with-angular-material

We install all the Angular Material dependencies helped by Angular CLI through the ng add command. It will add and register Angular Material in the project.

ng add @angular/material
datagrid-with-angular-material>ng add @angular/material
i Using package manager: npm
√ Found compatible package version: @angular/material@14.0.3.
√ Package information loaded.
    
The package @angular/material@14.0.3 will be installed and executed.
Would you like to proceed? Yes
√ Packages successfully installed.
? Choose a prebuilt theme name, or "custom" for a custom theme: Indigo/Pink        [ Preview:
https://material.angular.io?theme=indigo-pink ]
? Set up global Angular Material typography styles? Yes
? Include the Angular animations module? Include and enable animations
UPDATE package.json (1127 bytes)
√ Packages installed successfully.
UPDATE src/app/app.module.ts (423 bytes)
UPDATE angular.json (3380 bytes)
UPDATE src/index.html (595 bytes)
UPDATE src/styles.scss (181 bytes)

Angular Material modifies the application styles to match the Material Style Guide.

Next, modify the file app.module.ts where we must import MatTableModule.

import { NgModule } from  '@angular/core';
import { BrowserModule } from  '@angular/platform-browser';
import { MatTableModule } from  '@angular/material/table';
import { AppComponent } from  './app.component';
import { BrowserAnimationsModule } from  '@angular/platform-browser/animations';
 
@NgModule({
declarations: [
AppComponent
],

imports: [
	BrowserModule,
	BrowserAnimationsModule,
	MatTableModule
],

providers: [],
bootstrap: [AppComponent]
})

export  class  AppModule { }

Next, we are going to list the data in the table.

List Data With mat-table

We modify the file app.component.ts to add properties to the template and the mat-table, declaring a ViewChild mytable to reference the table in the template reference.

  @ViewChild(MatTable) mytable: MatTable<Article>;

Create the property columns to stores the names for each column using the matColumnDef:

  columns: string[] = ['name', position];

Next, we create the service NbaService because it uses the httpClient to import into the app.module. Then we create the method and, using the httpClient, call the API.

The API returns one array of NBA players in the property data to simplify our code return of an observable of any.

In the real world, we must map the data to the interface.

name: "Alex"
        id: 1
        last_name: "Abrines"
        position: "G"
    }
]

In the app.component.ts, we must inject the NbaService into the constructor and declare a new property dataSource to store the data from the nbaService.

export class AppComponent implements OnInit {
  dataSource : any;;
  constructor(private nbaService: NbaService) {

  }
}

In the ngOnInit lifecycle, subscribe to the nbaService and set the data to the datasource.

  ngOnInit(): void {
    this.nbaService.getData().subscribe((data) => {
      this.dataSource = data;
    });

Next, declare the markup for the table with the directive mat-table.

<table mat-table [dataSource]="datasource" class="mat-elevation-z8" #mytable>
	<tr mat-header-row *matHeaderRowDef="columns"></tr>
	<tr mat-row *matRowDef="let  row; columns: columns;"></tr>
    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef >Name</th>
      <td mat-cell *matCellDef="let t">{{ t.first_name }}</td>
    </ng-container>

    <ng-container matColumnDef="team">
      <th mat-header-cell *matHeaderCellDef>Position</th>
      <td mat-cell *matCellDef="let t">{{ t.position }}</td>
    </ng-container>

</table>

When we define the table tag, we specify the binding of the [dataSource] property with datasource defined in the class.

<table mat-table [dataSource]="datasource" class="mat-elevation-z8" #mytable>

For the columns, we define ng-container labels by initializing the matColumnDef property with one of the components of the columns attribute. We also create the column title as its content:

    <ng-container matColumnDef="FirstName">
      <th mat-header-cell *matHeaderCellDef>Name</th>
      <td mat-cell *matCellDef="let t">{{ t.first_name }}</td>
    </ng-container>
More about Angular Material Table.

Add Pagination

When we display a large amount of data, one nice feature is pagination. To provide the pagination, Angular Material gives us the mat-paginator.

First, we will import the module MatPaginatorModule into the modules.

import { MatPaginatorModule } from  '@angular/material/paginator';

@NgModule({
	declarations: [AppComponent],
	imports: [
		BrowserModule,
		BrowserAnimationsModule,
		MatTableModule,
		MatPaginatorModule,
	],

Edit the app.component.ts and add a new property for paginator ViewChild. To access the MatPagination from the template, update the code into the subscription and set to datasource a new instance of MatTableDataSource with the data from the subscription and the paginator viewchild.

@ViewChild(MatPaginator, { static:  true }) paginator!: MatPaginator;
ngOnInit(): void {
	    this.nbaService.getData().subscribe((data) => {
         this.dataSource = new MatTableDataSource<any>(data);
         this.dataSource.paginator = this.paginator;
    });

}

Next, update the template to connect the table with the datasource and configure the mat-paginator with the list of options for pagination. Adding the directive showFirstLastButton activates the navigation buttons to move from last to first.

<table  mat-table  [dataSource]="dataSource"  class="mat-elevation-z8"  #mytable>
	...
</table>
<mat-paginator  [pageSizeOptions]="[3, 5, 10]"  showFirstLastButtons></mat-paginator>

Add Sorting

To allow the users to sort the data, use MatSort. First, we import the MatSortModule in the app.module.

import { MatSortModule } from  '@angular/material/sort';

@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatSortModule,
MatTableModule,
MatPaginatorModule,
],
providers: [],
bootstrap: [AppComponent],
})

export  class  AppModule {}

First, add a viewchild for the paginator, import the MatSort into the app.component.ts, and declare a viewChild sort to link it with the template.

import { MatSort } from  '@angular/material/sort';

@ViewChild(MatSort, { static:  true }) sort!: MatSort;

On the ngOnInit lifecycle, assign to the Datasource sort property MathSort viewchild.

ngOnInit(): void {
	   this.nbaService.getData().subscribe((data) => {
         this.dataSource = new MatTableDataSource<any>(data);
         this.dataSource.paginator = this.paginator;
   this.dataSource.sort = this.sort;
    });
}

Edit the app.component.html, add the matSort directive to the table and add the mat-sort-header for each column.

<table  mat-table  [dataSource]="dataSource"  class="mat-elevation-z8"  #mytable  matSort>
<tr  mat-header-row  *matHeaderRowDef="columns"  ></tr>
<tr  mat-row  *matRowDef="let  row; columns: columns;"></tr>
    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
      <td mat-cell *matCellDef="let t">{{ t.first_name }}</td>
    </ng-container>

    <ng-container matColumnDef="team">
      <th mat-header-cell *matHeaderCellDef mat-sort-header>Position</th>
      <td mat-cell *matCellDef="let t">{{ t.position }}</td>
    </ng-container>
</table>

Add Filtering

Sadly Angular Material does not ship with a particular component or filter directive. To solve this problem, we must implement the data filtering manually.

We define a method called filter that is executed each time the user enters or deletes a character in the mat-input control:

  filter(event: Event) {
    const filter = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filter.trim().toLowerCase();
  }  

When we initialize the filter property of dataSource, the data displayed in the view is updated.

<input  matInput  (keyup)="filter($event)"  placeholder="find">

Result:

Angular Material Datagrid

Boost Performance

Sometimes we have to display an extensive list of data in a table or list. Adding all of them in the DOM can cause problems and force the application to slow down.

The Angular CDK provides a virtual scroll to display only a small subset of the items in the viewport. It keeps the number of DOM elements constant, boosting the application’s performance.

Unfortunately, the virtual scroll CDK does not work by default with mat-table, but the package https://github.com/diprokon/ng-table-virtual-scroll (not official) is a directive that allows the use of virtual scrolling in mat-table.

import { MatSortModule } from  '@angular/material/sort';
import { ScrollingModule } from  '@angular/cdk/scrolling';
import { TableVirtualScrollModule } from  'ng-table-virtual-scroll';

@NgModule({
declarations: [AppComponent],

imports: [
BrowserModule,
BrowserAnimationsModule,
MatSortModule,
MatTableModule,
MatPaginatorModule,
ScrollingModule,
TableVirtualScrollModule,
],
providers: [],
bootstrap: [AppComponent],
})

export  class  AppModule {},

Change the MatTableDataSource to TableVirtualScrollDataSource.

    this.nbaService.getData().subscribe((data) => {
      this.dataSource = new TableVirtualScrollDataSource(data);
    });

Note: ng-table-virtual-scroll is not the official package.

Edit the template:

<cdk-virtual-scroll-viewport
tvsItemSize="30"
headerHeight="56"
class="wrapper mat-elevation-z2"
style="height: 400px"
>
....
</cdk-virtual-scroll-viewport>

Conclusion

In this article, we had the chance to use Angular Material to build a data list. Angular Material provides a few components and directives for creating dynamic applications. However, not all the features come out of the box, and sometimes we need to add custom solutions like filtering or using extra packages like the ng-table-virtual-scroll.

See the full code example for this article and play with the example app at the following links:

Next, we’ll compare this process with building a data grid using Kendo UI for Angular.


About the Author

Dany Paredes

Dany Paredes is a Google Developer Expert on Angular and Progress Champion. He loves sharing content and writing articles about Angular, TypeScript and testing on his blog and on Twitter (@danywalls).

Related Posts

Comments

Comments are disabled in preview mode.