How to create an Angular Material data table with scrolling rows and a fixed header

Visit Stack Blitz Demo

I'm currently facing a challenge with getting my code to function as required.

In the demonstration provided, there is a table where clicking the add button inserts a new row. Once the table reaches a certain height (let's say after adding 5 rows), I need a vertical scroll bar to appear while maintaining a sticky header. Essentially, I want to set a fixed height for the table rows.

Refer to the attached screenshot for the desired location of the scroll bar placement, despite the shaky highlight.

The HTML structure is outlined below:

<div class="table-container">
  <table mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="category">
      ...
    </ng-container>
    ...
  </table>
   <div *ngIf="dataSource.filteredData.length > 0" > <mat-paginator [pageSizeOptions]="[5]" showFirstLastButtons class="paginator"></mat-paginator></div>
</div>

Component Add Function:

addRow() {
    this.doAddRow();
    this.expanded = false;
  }
  private doAddRow() {
    ELEMENT_DATA.push({ code: '', type: '', reference: '' });
    this.dataSource = new MatTableDataSource(ELEMENT_DATA);
  }

https://i.sstatic.net/v6fqQ.png

Answer №1

To resolve this issue, you need to insert a div element outside of your table and define a min-height property.

app.component.css

.main-table{
  max-height:42vh;
  overflow-y:scroll;
  overflow-x:hidden;
}

app.component.html

<ol>
  <li>Click on + to add new rows</li>
</ol> 
<p>Once 5 new rows are added, a vertical scroll bar should appear, and when scrolling, the header row should remain visible.</p>
<div class="table-container">
  <div class="main-table">
  <table mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="category">
      <th mat-header-cell *matHeaderCellDef >Category</th>
      <td mat-cell *matCellDef="let code" >
        <mat-form-field class="code">
          <mat-select>
            <mat-option *ngFor=" let category of categories" [value]="category.code" class="dropdownpPopUp"  (keydown.ArrowDown)="onDown()">{{category.code}}</mat-option>
          </mat-select>
        </mat-form-field>
      </td>
    </ng-container>
    <ng-container matColumnDef="type">
      <th mat-header-cell *matHeaderCellDef>Type</th>
      <td mat-cell *matCellDef="let code" >
        <mat-form-field class="type">
          <mat-select >
            <mat-option *ngFor=" let type of types" [value]="type.code" class="dropdownpPopUp"  (keydown.ArrowDown)="onDown()">{{type.code}}</mat-option>
          </mat-select>
        </mat-form-field>
      </td>
    </ng-container>
    <ng-container matColumnDef="additionalCode" class="parent" >
      <th mat-header-cell *matHeaderCellDef>Additional Code</th>
      <td mat-cell *matCellDef="let element" class="parent" >
        <mat-form-field class="type">
          <input matInput (keyup)="toggleLookup($event)" autocomplete="off" (keydown.ArrowDown)="onDown()">
        </mat-form-field>
        <div *ngIf="expanded" class="child">Yes, it has expanded
          <button (click)="expanded = false">Close</button>
        </div>
      </td>
    </ng-container>
    <ng-container matColumnDef="ref">
      <th mat-header-cell *matHeaderCellDef>Reference</th>
      <td mat-cell *matCellDef="let element" >
        <mat-form-field>
          <input matInput [(ngModel)]="element.type"  (keydown.ArrowDown)="onDown()" autocomplete="off">
        </mat-form-field>
      </td>
    </ng-container>
    <ng-container matColumnDef="add">
      <th mat-header-cell *matHeaderCellDef>
        <button mat-icon-button (click)="addRow()" matTooltip="Add Row">
          <mat-icon>add</mat-icon>
        </button>
      </th>
      <td mat-cell *matCellDef="let code; let i = index;">
        <button mat-icon-button (click)="removeAt(i)" matTooltip="Remove Row">
          <mat-icon>clear</mat-icon>
        </button>
      </td>
    </ng-container>
    <tr mat-header-row *matHeaderRowDef="columns"></tr>
    <tr mat-row *matRowDef="let rows; columns: columns;"></tr>
  </table>
  </div>
   <div *ngIf="dataSource.filteredData.length > 0" > <mat-paginator [pageSizeOptions]="[5]" showFirstLastButtons class="paginator"></mat-paginator></div>
</div>

Answer №2

Indeed, the Angular Material guidance has addressed this topic.

Through the use of position: sticky styling, it is possible to fix the table's rows and columns in place so they remain visible even when scrolling. This feature provides inputs that automatically apply the necessary CSS styling for achieving sticky rows and columns.

To implement this functionality, you simply need to update your <tr> tag with the mat-header-row directive as follows:

<tr mat-header-row *matHeaderRowDef="columns; sticky: true"></tr>

Furthermore, ensure that the container class surrounding your table includes the following properties:

.table-container {
  height: 400px;
  overflow: auto;
  /*
  other CSS properties
   */
}

By incorporating these adjustments, your material datatable will feature a sticky header. Check out the live demo provided in the official documentation for reference.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to ensure the hamburger menu stays perfectly centered?

My menu is currently aligned to the right and scrolls with the user profile. I want the menu to always be centered and the profile to stay on the right side. I am working with Angular 12 and Bootstrap 5 for this project. Here is the code I have been usin ...

Elements with absolute positioning are preventing drag events from executing

Struggling to create a slider and encountering an issue. The problem lies in absolute items blocking slider drag events. I need a solution that allows dragging the underlying image through absolute positioned items. Any ideas on how to achieve this? MANY T ...

Openshift is unable to locate the CSS file I uploaded

I successfully created a new app on Openshift and the default template is functioning well. The only customization I made was: <head> ... <link href="styles.css" rel="stylesheet"> </head> and placed a file named styles.css in the same d ...

Opting out of notifications using Angular's NGXS

I'm new to NGXS in Angular and have recently learned that you don't need to manually unsubscribe when using the async pipe. However, I am currently subscribing to both query parameters and dispatched actions. Do I still need to manually unsubscri ...

javascript if condition not executing properly

I am struggling with my random number generator that should generate numbers between 5 and 15. I am trying to change the position of the 'chest' div based on the number generated by the computer, but for some reason it is not working as expected. ...

Investigating Jquery Flip Card Issues

Looking to create a set of flip cards using HTML, CSS, and jQuery. Currently facing an issue where only the first card is flipping when clicked. Any suggestions on how to modify the jQuery code to make it work for all cards would be highly appreciated. C ...

A beginner's guide to setting up Bootstrap on Angular 8

Struggling with installing Bootstrap in Angular 8? Followed multiple guides but still facing issues? Check out this helpful article on getting-started for a step-by-step guide. ...

"Troubleshooting a CSS Navigation Bar Image Glitch

As I was experimenting with my navbar, I encountered an issue when trying to add an image right before the username. This is not something I usually deal with as I focus more on server-side scripting than design aspects. The active class on the dropdown ...

The Angular 4 click event with jQuery is failing to work properly within an HTTP get request

I'm having trouble with the code I used. When the PHP call succeeds, I trigger the click event but it's not working inside the HTTP GET method. However, if I place it outside the HTTP method, it works fine. In my component.html file: <a #myD ...

Stop form from submitting on bootstrap input, still check for valid input

Encountering a dilemma here: Employing bootstrap form for user input and utilizing jQuery's preventDefault() to halt the form submission process (relying on AJAX instead). Yet, this approach hinders the input validation functionality provided by boots ...

Syncing a line's position with the cursor in Angular using the ChartJs Annotation Plugin

I've been working on creating a crosshair using the annotation plugin, and while I've been able to modify the line's value, it doesn't seem to update on the chart. Here are the details of my chart options : public financialChartOptions ...

Why does my application handle arrays as strings from the backend?

After storing a column as an array, I encountered an issue where Ionic was treating it as a string instead of an array when received from the server. This is how the data is stored: ["loca6_1.jpeg","loca6_1.jpeg"] The backend function ...

Continuous CSS animations with enduring final state

I have a div element serving as the main container for my webpage. Inside this, there is another div element which functions as a curtain - when activated, it covers the entire page with a dark semi-transparent layer similar to a lightbox. This deactivates ...

Dynamic Angular components and their content projection

Can content projection be utilized in Angular 2 components that are dynamically loaded and created at runtime, rather than being known at compilation time? ...

Checking if a date is before another date in MomentJS without considering the

Recently, I've been utilizing momentJS to identify future dates without a specific day. Below is the code I've been working with along with the outcome: moment('09/2010').isBefore(moment().format('MM/YYYY'), 'day') ...

Using Angular directives to pre-select a default option

I am currently working on a select HTML tag with two options, and I want to set the first option as the default choice. My select element includes Angular directives like ng-change and ng-model. I have attempted to achieve this by adding selected = "select ...

Tips for utilizing specific backend properties exclusively in an ngrx store

As I embark on incorporating ngrx into a new enterprise Angular application, I am faced with the task of loading data into the store using a simple effect that triggers a service call. The response from the server upon successfully calling this service co ...

Utilize Angular to Transfer HTTP Array Data from a Service to a Component

As I work on developing an app with Angular, my current challenge involves a service that retrieves an Array of Data from an online source. My goal is to make this array accessible in other components but I'm facing difficulty in passing the data to t ...

CSS - Maximizing One Column's Width While Allocating Percentage Space for Two Columns?

It's been quite some time since I delved into the world of web development and I seem to have forgotten how to tackle this particular issue. Despite hours spent searching online, I've gained knowledge about flex-boxes and other useful tools, but ...

Encountered an error with @angular-cli/ast-tools during the installation of angular-cli on a Mac running OS X (El Capitan

While attempting to install the latest version of @angular-cli on my Mac OS X (El Capitan) using the command sudo npm install -g @angular-cli@latest, I encountered the following error: Darwin 15.4.0 npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" ...