Search code examples
angularhtml-tablesum

How to calculate sum of table columns and show in footer using Angular?


I am trying to show the total of the column values in table footer using Angular. enter image description here

 <mat-header-row class="sticky-header" *matHeaderRowDef="['player', 'team', 'goals']"></mat-header-row>
      <mat-row *matRowDef="let row; columns: ['player', 'team', 'goals']"></mat-row>
      <mat-row class="sticky-footer" *matRowDef="let row: columns: ['total']; when:isLastRow"></mat-row>

...

 export class AppComponent {

  dataSource: PlayerDataSource;

  isLastRow = (data, index) => index === this.players.length;

  players = STATS.slice();

  constructor() {
    this.dataSource = new PlayerDataSource();
    this.dataSource.use(this.players.slice());
  }

}

After reading this github topic I created this stackblitz example but the sum is not displayed in the footer.

Can anyone shed some light on this subject? There is no example regarding this. Thank you.


Solution

  • There are instructions in the angular material documentation and also a sample in the examples.

    What you need to do is define a footer cell in a similar fashion you do for the headers in every column. In the column bindings for the footer column you define directly how you calculate the sum. There is no need to add another row with the total data. After that you just add also a footer row definition and it all works.

    Here is the changed template from your sample:

    <mat-table [dataSource]="dataSource">
    
      <!-- Columns -->
      <ng-container matColumnDef="player">
        <mat-header-cell *matHeaderCellDef> Player </mat-header-cell>
        <mat-cell *matCellDef="let player"> {{ player.name }}</mat-cell>
        <mat-footer-cell *matFooterCellDef></mat-footer-cell>
      </ng-container>
    
      <ng-container matColumnDef="team">
        <mat-header-cell *matHeaderCellDef> Team </mat-header-cell>
        <mat-cell *matCellDef="let player"> {{ player.team }}</mat-cell>
        <mat-footer-cell *matFooterCellDef></mat-footer-cell>
      </ng-container>
    
      <ng-container matColumnDef="goals">
        <mat-header-cell class="right-align" *matHeaderCellDef> Goals </mat-header-cell>
        <mat-cell class="right-align" *matCellDef="let player"> {{ player.goals }}</mat-cell>
        <mat-footer-cell *matFooterCellDef> Total: {{ calculateTotal() }}</mat-footer-cell>
      </ng-container>
    
      <!-- Rows -->
      <mat-header-row class="sticky-header" *matHeaderRowDef="['player', 'team', 'goals']"></mat-header-row>
      <mat-row *matRowDef="let row; columns: ['player', 'team', 'goals']"></mat-row>
      <mat-footer-row class="sticky-footer" *matFooterRowDef="['player', 'team', 'goals']"></mat-footer-row>
    
    </mat-table>
    

    And also the changed component code so you see you do not need to modify the data.

    export class AppComponent {
    
      dataSource: PlayerDataSource;
    
      isLastRow = (data, index) => index === this.players.length;
    
      players = STATS.slice();
    
      constructor() {
        this.dataSource = new PlayerDataSource();
        this.dataSource.use(this.players.slice());
      }
    
      public calculateTotal() {
        return this.players.reduce((accum, curr) => accum + curr.goals, 0);
      }
    
    }
    
    
    export class PlayerDataSource extends DataSource<PlayerOrTotal> {
    
      dataWithTotal = new BehaviorSubject<PlayerOrTotal[]>([]);
    
      use(players: Player[]) {
        this.dataWithTotal.next([ ...players]);
      }
    
      connect(): Observable<PlayerOrTotal[]> {
        return this.dataWithTotal.asObservable();
      }
    
      disconnect() {}
    }
    

    I have created also a fork of your StackBlitz where you can see it working.