avatarMichelle Wiginton

Summary

The article provides a comprehensive guide on creating a table with expandable and collapsible rows in Angular, utilizing an open-source Elden Ring API for data population.

Abstract

The author of the article shares their experience in developing an Angular table with expandable and collapsible rows, a feature useful for managing large datasets without overwhelming the user. The guide includes steps for setting up a new Angular project, installing necessary dependencies like Angular Material and Angular Animations, and configuring routing. It also covers the creation of a service class to fetch data from an Elden Ring API, implementing animations for row expansion, and defining the table structure in both HTML and TypeScript. The article aims to fill a gap observed in the Angular documentation by providing a detailed explanation and code examples, including routing and API integration, to help developers implement similar tables in their projects.

Opinions

  • The author found the Angular documentation lacking in detailed explanations for creating tables with expandable rows, which motivated them to write this article.
  • After experimentation and research, the author arrived at a solution that met their needs and decided to share it with the community.
  • The author believes that using a table with expandable rows is beneficial for displaying large amounts of data in a user-friendly manner.
  • The article suggests that the provided example can be a starting point for others and encourages feedback for improvement.
  • The author promotes signing up for Medium through their referral link to support their work and access unlimited articles from various authors.
  • The author recommends an AI service, ZAI.chat, as a cost-effective alternative to ChatGPT Plus (GPT-4), highlighting its affordability and performance.

A Table with Expandable and Collapsible Rows Using Angular

In this article, I will walk you through how I created a table in Angular that has expandable and collapsible rows. My motivation for writing this article is based on personal experience I had when trying to create such a table for a project. I found some code samples in the Angular documentation but not a whole lot of explanation as far as how things were working. So I found myself a bit confused looking at the example and unsure of how I could take this information and apply it to my use case.

After lots of experimentation and trial and error as well as some other examples I was able to find, I arrived at a solution that fit my needs. My hope is that this article can provide a good explanation for how to build a table in Angular with expandable and collapsible rows.

One reason for wanting to make use of a table with expandable rows is if you are in a situation where you want to display lots of data, but you may not want to overwhelm people by displaying all of the data at once. The outer portion of your table row can serve as a header while the expandable portion, when clicked, can serve as the portion that contains the additional detail that you are wanting to conditionally display.

For this demonstration, I decided to use an open source fan made Elden Ring API to populate the table we will see in our example (https://eldenring.fanapis.com/). The repository for this example can be found here. Let’s now look at how we can create such an example and how it works.

New Project Setup

The first thing you will want to do is create a new Angular project. You can do so by running the following command ng new angular-expand-collapse-example. You can title your project anything you would like. In the case of our example, our project is titled angular-table-expand-collapse-example

Once your project has been created, navigate into your project directory by executing the command cd angular-expand-collapse-example. From within this directory, you will want to install the Angular material and Angular animations dependencies as we will need both of these in order to create our table as well as the animations for expanding and collapsing rows.

npm install --save @angular/material
npm install --save @angular/animations

Once these dependencies have been successfully installed, you may then proceed to import the BrowserAnimationModule into your app.module.ts file. We’ll later import the MatTableModule into a separate module which we will create.

import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@NgModule({
...
  imports: [BrowserAnimationsModule],
...
})
export class AppModule { }

Setting up our Component and Module

The next step in our project will be to create the component where we will be creating our table. I created my component in a subdirectory pages but it can be created in whatever structure works best for you. We generate a component by executing the following command ng generate component pages/table-expand-collapse

Once our component has been created we will create a new module for this component. The module name in this case is titled table-expand-collapse.module.ts and is located in our table-expand-collapse directory along with the component. In this module we can also import our MatTableModule. The newly created module will look as follows

Set up Basic Routing

Now that we have created a module for our component, let’s set up our basic routing functionality. In our app directory, we can create our app-routing.module.ts file as seen in the example below.

Our AppRoutingModule then needs to be imported in our app.module.ts

import { AppRoutingModule } from './app-routing.module';
@NgModule({
...
  imports: [AppRoutingModule],
...
})
export class AppModule { }

Our next step is to create our routing module for our TableExpandCollapseComponent. Our file in this example is called table-expand-collapse-routing.module.ts and appears as follows

The final steps in configuring our routing are to import our TableExpandCollapseRoutingModule in our TableExpandCollapseModule as follows

import { TableExpandCollapseRoutingModule } from './table-expand-collapse-routing.module';
@NgModule({
...
  imports: [TableExpandCollapseRoutingModule],
...
})
export class TableExpandCollapseModule{ }

And lastly in our app.component.html file removing all of our boilerplate contents and just including the following <router-outlet></router-outlet>

Creating our Service Class

The last step we need to perform before getting into the meat of setting up our table is to create our service class. Our service class is where we will consume our API that ultimately fetches the data that we will be using to populate our table. Our API we are using in this case is the mentioned fan made open source Elden Ring API and we will be using it to retrieve a list of bosses in Elden Ring to display in our table. In our example, our service class is created in a subdirectory services. We can generate a new service by executing the following command ng generate service services/table

Within our TableService class we can create our function getBosses() which will consume our API to retrieve a list of Elden Ring bosses. The service in our demo will appear as follows

You’ll notice that in order to make http requests, we needed to import the HttpClientin our service file. We also need to import the HttpClientModule in our AppModule

import { HttpClientModule } from '@angular/common/http';
@NgModule({
...
  imports: [HttpClientModule],
...
})
export class AppModule { }

Creating our Table

Now that we have a basic project set up with our routing, component, and service, we can now work on the logic to create our table. In our table-expand-collapse.component.html file, we can see the html code that is responsible for rendering and displaying our table on the web page. At the very top of the file, our table is defined using mat-table which let’s us know this is an Angular material table. Next we can see our dataSource which contains the data used to populate our table. The last keyword you will see at the top of our table definition is multiTemplateDataRows. This is let’s us know that we will have a section for expandable rows within our table. Full table definition: <table mat-table [dataSource]=”bossesDataSource” multiTemplateDataRows></table>

Next let’s take a look at our component file for this table table-expand-collapse-component.ts. On line 10 of our file we are defining an animations property which contains a trigger definition detailExpand. This trigger is responsible for keeping track of whether or not an element is expanded.

animations: [
   trigger('detailExpand', [
   state('collapsed', style({height: '0px', minHeight: '0'})),
   state('expanded', style({height: '*'})),
   transition('expanded <=> collapsed', animate('225ms cubic
     bezier(0.4, 0.0, 0.2, 1)')),
   ]),
]

Starting at line 20 of this file we are declaring an array bossColumns. This is an array which contains the column definitions for our table. In our case, our table will have two columns: a name column and a region column. Next we declare an array bosses, which will be responsible for storing the API response that we will use in displaying our table data. Our last declaration is our bossesDataSource. This will be responsible for storing the contents of our bosses array, then making this information visible from our table.

Now let’s look at our ngOnInit() method which will get called as soon as our page loads. You can see within this function, we are calling the function getBosses(). This function is responsible for calling our API, storing the response of our API call into our bosses array, and then initializing our bossesDataSource to include the contents of this array. You’ll notice in the sake of our example that our API returns 106 results, but to keep this simple, we are just filtering our API response to include 5 bosses by their appropriate names. Once we have our bosses array containing our 5 elements, we then use the map function to add an isExpanded property to each element in our array. This will be used to keep track of whether or not an element is expanded.

Now that we’ve finished inspecting our component, let’s jump back over to our html file and look at the rest of the contents of our table. You’ll see that after defining our table, we next define each column of our table based on the string array of column names defined in our component. One example of a column definition as follows:

<ng-container matColumnDef="name">
   <th mat-header-cell *matHeaderCellDef>Name</th>
   <td mat-cell *matCellDef="let boss">
      <span>{{ boss.name }}</span>
   </td>
</ng-container>

After we declare each of our table rows, we then move onto declare our expanded column defined by expandedDetail. This will display the additional information once the row has been clicked and expanded. You’ll also note that this is where we are referencing our detailExpand trigger defined in our component.

Now that we’ve defined our columns and all of the data that they will contain, we can now define the row that will display our column headers:

<tr mat-header-row *matHeaderRowDef="bossColumns"></tr>

Then we can define the row that will display all of the actual data. This will also contain our (click) event that will either expand or collapse the row based on the current status:

<tr mat-row *matRowDef="let boss; columns: bossColumns;"
   class="example-element-row"
   [class.example-expanded-row]="boss.isExpanded"
   (click)="boss.isExpanded = !boss.isExpanded">
</tr>

The final row definition will be responsible for displaying the information in our expandedDetail:

<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>

Wrapping Up

This concludes the steps necessary needed to create table with expandable and collapsible rows in this example. The final product will look like this when the page first loads:

Live demo of a table with expandable and collapsible rows

I hope this example has been helpful for anyone that needs assistance on similar problems. Feel free to share any feedback on how this example can be improved. Original documentation: https://material.angular.io/components/table/examples

If you enjoyed reading this article, please consider signing up for Medium if you haven’t already done so using my referral link. This subscription ensures unlimited access to my articles plus thousands of other talented writers across many disciplines.

Angular
JavaScript
Web Development
Software Engineering
Programming
Recommended from ReadMedium