Ross
Ross

Reputation: 415

Making an Angular Material data table rows scroll while maintaining a sticky a header

Stack Blitz Demo

I am having trouble figuring out how to get my code to behave as needed.

As the demo shows I have a table that when the add button is clicked a new row is added. Once the table rows reach a certain height (say for example after adding 5 rows) I need a vertical scroll bar to display and at the same time I need to maintain a sticky header. Basically I need to set the table rows to a fixed height.

The attached screen shot shows where I would like the scroll bar to display. Apologies for the shaky highlight.

The HTML looks looks like this

<div class="table-container">
  <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">Yah it 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 *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);
  }

enter image description here

Upvotes: 1

Views: 11622

Answers (2)

wentjun
wentjun

Reputation: 42516

Actually, Angular Material documentation has covered that.

By using position: sticky styling, the table's rows and columns can be fixed so that they do not leave the viewport even when scrolled. The table provides inputs that will automatically apply the correct CSS styling so that the rows and columns become sticky.

You just need to make the following changes to your <tr> tag with the mat-header-row directive.

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

In addition, you will need to edit your container class around your table to include the following properties:

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

This will allow your material datatable to have a sticky header. Here is the demo provided by the official documentation.

Upvotes: 2

piedpiper
piedpiper

Reputation: 524

what you have to do is add a div outside of your table and provide a min-height property.

app.component.css

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

app.component.html

<ol>
  <li>Select + to add rosw</li>
</ol> 
<p>After 5 new rows added a vertical scroll bar should aprear and when srolling the header row should still be 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">Yah it 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>

Upvotes: 2

Related Questions