How to build a dynamic table component in Angular 9?

If you are working on an Angular project, there will be several pages where data is represented using a table structure.

One of the libraries we can leverage is the Angular material library (material.angular.io). The material library’s table component will not only make our lives easy to build a table with the given data but also seamlessly integrate basic functionalities such as Pagination, Sorting and Search operations 😍.

To be in line with the DRY (Don’t Repeat Your Code) principle, let us develop a dynamic mat-table component and reuse it where ever necessary.

Without further ado, let’s jump into the code to build a dynamic material table component that can be used in components across the project 🙈.

First of all, create an Angular project using the ng new <project_name> command. The latest stable version of the angular is V9.0.6

Secondly, install the angular-material library by running the command npm install --save @angular/material in the project directory. The latest version of angular-material is V9.1.2

package.json

Let us create a table component using the command ng g c mat-table which will update the app.module.ts component’s declarations to accommodate the mat-table component.

FYI: In the above command ng g c <component_name>; g is short for generate and c is short for component

At this point, we should go ahead to import the MatTableModule into the app.module.ts. The following code will have both the table module and component imported into the project. If you see an error ‘Can’t find the module @angular/cdk/table" go ahead and install the @anglular/cdk using the command npm i @angular/cdk.

AppModule.ts

Go ahead and run the project using ng serve -o to start the project in a new tab if you have not already started.

Note: If you are using angular V8.x in your project, don’t worry if you come across an error: “error TS1086: An accessor cannot be declared in an ambient context.”. When you have installed the angular/material, the CLI would have fetched the latest V 9.X from the npm repository. To resolve this error, make sure that your @angular/cli and @angular/core versions are inline with @angular/material version by using ng update @angular/core @angular/cli command. You can check package.json whether core, CLI and material lib versions are in ^9.0.0 version.

The above will integrate the material lib setup into our project.

At this point, navigate to the mat-table component to develop the basic table structure. We can come with mock-data that can be used to display in the table. Also, initialize the mock-data using new MatTableDataSource() constructor, we will discuss the reason down the line.

Let us build the HTML side of the table to render the data. The following code will do the job for now:

Render the mat-table component in the app.component.ts using the selector tag <app-mat-table></app-mat-table> to display the table

The following is the output that will be displayed in the browser at the localhost:4200 :

Browser output

At this point, if we have to retrieve a table with 15–25 columns, we will have to repeat the <ng-container></ng-container> piece of code in mat-table. component.html for as many columns. It will be difficult to maintain the HTML code in the long run.

We can overcome this issue by automating the <ng-container> piece of code to render as many columns as we use in the tableData. To achieve this, go-ahead to the mat-table.component.html and make the following changes:

From the above code, we have looped through a list of tableCols to assign the matColumnDef property in the th tag. We have used the column name key to fetch the respective value from the matCellDef’s profile object which in this case it the row data.

Note: titlecase is a built-in pipe provided by Angular to make the first letter of a word capital letter

Can you think of the number of lines of HTML code the above code would save if we have 7–15 columns to display, which is the reality of 60% of the cases in enterprise apps? One more thing to keep in mind is, if we have to add a new column, we can simply add that to each record in the tableData property in the mat-table.component.ts and update the tableCols with the new column name. That is it, the new data will be rendered in the table without adding <ng-container>...</ng-container> a piece of code for every new column in the HTML scheme of things.

At this point, I hope you are on the same page as I am. Feel free code along for a better experience.

The reason to use MatTableDataSource is, it’s easier to integrate sorting, pagination and searching to the table. Else we need to perform these operations manually by defining the respective functions to modify the data and update the view accordingly.

Let’s go ahead and add Sorting functionality to the table component. Inside the mat-table.component.ts let’s add the following line of code:

@ViewChild(MatSort, {static: true}) sort: MatSort; I hope you are aware of the ViewChild decorator from Angular; if not please refer to https://angular.io/api/core/ViewChild

Hey, I want you to pause and think here for a moment. Are we missing anything 🤔? Hmm….Wait what? Hell yeah, how can I not add MatSortModule from @angular/material/sort to the app.module.ts to make the sort directive work?😎

Furthermore, assigning the tableDateSrc’s sort to above declared ViewChild sort property as follow:

Updated mat-table.component.ts for sorting

Also, we have to update the code in html by adding matSort directive to the <table> tag and mat-sort-header to the th tag as follows:

mat-table.component.html with sorting

Also, keep in mind, if we have not automated the table’s <ng-container> part of code, we have to update every column with the mat-sort-header directive.

At this point of time, go ahead and play around with the table headers in the browser. You may notice an error in the browser console, it may be complaining about the arrow animations. To overcome this error import the BrowserAnimationsModule in the app.module.ts

If you remember the roadmap, the next task is to integrate the Pagination functionality to the table. Let’s go get that working 😃

For this task, we need to add <mat-paginator> tag to the table HTML as follows:

Update mat-table.component.html for Pagination

Similar to updating the sort property in tableDataSrc, we need to update the paginator property to integrate Pagination. The following will help understand the same:

Update mat-table.component.ts for Pagination

I know you’ll be thinking about whether there is a MatPaginatorModule . Take a bow, you’re on the right track. Go ahead and update the app.module.ts

Now, let’s integrate Search functionality to the mat-table:

Updated mat-table.component.html with search functionality

mat-table.component.ts is updated with the onSearchInput() fn as follows:

Updated mat-table.component.ts with search functionality

To make the table data and table column names dynamic, update the mat-table.component.ts code as to below:

Update the tableData and tabCols to be dynamic inputs using @Input()

If you observe the above code, we are no longer getting the data from the mat-table.component.ts rather we are feeding data from the app.component.ts

Have a look at the app.component.ts/html code:

app.component.ts with the table data
Look at the input tags tableData and tableColumns inputs

Boom!!👻 we have completed coding the dynamic mat-table component with Sort, Pagination and Search functionality.

Final output in the browser after integrating styles and font

If we place one more <mat-table> selector in the app.component.html with a different data fed to the mat-table we see the following rendered in the browser:

Browser rendering different data with the same mat-table component

The following is the code to render the above table:

app-component.html
second table data in the app-component.ts

From the above code, we are sure that irrespective of the number of columns and data fed to the mat-table component, it can render the table in the browser. We can use the same analogy in different components across the project.

I know this is a lengthy article 😔. Let’s continue in the next article to enhance the table code to dynamically provide options to have checkboxes to select a few records to perform actions 😃. Moreover, we will also integrate the ability to have a dynamic button for each row to view record specific details using angular routing.

Thanks for your time, I hope you have learnt something new and useful. Until then take care, have a good rest of your day 🙃

You can find the code in my GitHub: https://github.com/HarshaChinni/ng-dynamic-material-table

FYI: You need to import Roboto font and material themes for material-library styles to work properly. Please go through the index.html and styles.css files to understand further.

References:

  1. https://angular.io/docs
  2. https://material.angular.io/components/table/overview#getting-started
  3. https://material.angular.io/guide/getting-started