Table inside row and Multiple material Paginator's in angular material table


Hey guys, in this post we are going to see how to apply multiple angular material paginators when we are having multiple angular material tables. And very important is, here we are adding tables inside the other table’s row. Last time we saw how to make expandable rows (click here for expandable rows - example) and in the same example, we are going to add a table inside that expandable row.  Let’s do it step by step.

[ If you are preparing for an interview please click here - angular interview questions and answers ]
[If you have any problem related to the Angular material table please check our following posts:  
Click here for a simple material table with searching, sorting, and pagination, 
Click here for inline editing and validation with template-driven forms in the angular material table, 
Click here for multiple expandable rows in the angular material table, 
Click here for add remove columns dynamically in the angular material table]

Same as the last example we have a material table which displays the student details but here we have added another material table inside an expandable row which displays the test and mark the details of each student.

We can apply material paginator to the primary table very easily as shown below, but it is a little difficult to add material paginator to the material table which is inside a row. Because a user can expand multiple rows and we will get multiple paginators, below we have added paginator to the child material table in HTML it looks single paginator but, runtime it is iterated and get added multiple time. So we need to handle that in the typescript file.



app.component.html
<div class="row">
    <div class="col-12 text-center">
        <label for="Heading" class="form-control">
            material table inside row , multiple paginators - angular Material Table
        </label>
    </div>
</div>
<div class="row justify-content-center">
    <div class="col-8">
        <div class="example-container mat-elevation-z8">
            <mat-table #table [dataSource]="dataSource" multiTemplateDataRows>

                <!-- Expand Column -->
                <ng-container matColumnDef="expand">
                    <mat-header-cell *matHeaderCellDef> Expand </mat-header-cell>
                    <mat-cell *matCellDef="let element">
                        <span *ngIf="!element.isExpanded" class="signs">+</span>
                        <span *ngIf="element.isExpanded" class="signs">-</span>
                    </mat-cell>
                </ng-container>

                <!-- Roll No Column -->
                <ng-container matColumnDef="rollNo">
                    <mat-header-cell *matHeaderCellDef> Roll No </mat-header-cell>
                    <mat-cell *matCellDef="let element"> {{element.rollNo}} </mat-cell>
                </ng-container>

                <!-- Name Column -->
                <ng-container matColumnDef="name">
                    <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
                    <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
                </ng-container>

                <!-- Marks Column -->
                <ng-container matColumnDef="marks">
                    <mat-header-cell *matHeaderCellDef> Marks </mat-header-cell>
                    <mat-cell *matCellDef="let element"> {{element.marks}} </mat-cell>
                </ng-container>

                <!-- Standard Column -->
                <ng-container matColumnDef="standard">
                    <mat-header-cell *matHeaderCellDef> Standard </mat-header-cell>
                    <mat-cell *matCellDef="let element"> {{element.standard}} </mat-cell>
                </ng-container>

                <!-- Expanded Content Column - The detail row is made up of this one column -->
                <ng-container matColumnDef="expandedDetail">
                    <mat-cell class="cellColor" [attr.colspan]="displayedColumns.length"
                        *matCellDef="let detail; let i = dataIndex">
                        <div class="innerTable">
                            <mat-table class="table-striped" #table [dataSource]="dataSourceSecondTable[i]"
                                multiTemplateDataRows>

                                <!-- Roll No Column -->
                                <ng-container matColumnDef="test">
                                    <mat-header-cell *matHeaderCellDef> Test </mat-header-cell>
                                    <mat-cell *matCellDef="let element"> {{element.test}} </mat-cell>
                                </ng-container>

                                <!-- Name Column -->
                                <ng-container matColumnDef="marathi">
                                    <mat-header-cell *matHeaderCellDef> Marathi </mat-header-cell>
                                    <mat-cell *matCellDef="let element"> {{element.marathi}} </mat-cell>
                                </ng-container>

                                <!-- Marks Column -->
                                <ng-container matColumnDef="english">
                                    <mat-header-cell *matHeaderCellDef> English </mat-header-cell>
                                    <mat-cell *matCellDef="let element"> {{element.english}} </mat-cell>
                                </ng-container>

                                <!-- Standard Column -->
                                <ng-container matColumnDef="maths">
                                    <mat-header-cell *matHeaderCellDef> Maths </mat-header-cell>
                                    <mat-cell *matCellDef="let element"> {{element.maths}} </mat-cell>
                                </ng-container>

                                <mat-header-row *matHeaderRowDef="displayedColumnsSecondTable"></mat-header-row>
                                <mat-row *matRowDef="let row; columns: displayedColumnsSecondTable;" matRipple
                                    class="element-row"></mat-row>
                            </mat-table>
                            <mat-paginator #subTablePaginator [pageSizeOptions]="[5, 10, 15]" showFirstLastButtons>
                            </mat-paginator>
                        </div>
                    </mat-cell>
                </ng-container>

                <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
                <mat-row *matRowDef="let row; columns: displayedColumns; let rInd = dataIndex" matRipple
                    class="element-row" [class.expanded]="row.isExpanded" (click)="expandCollapse(row, rInd)"></mat-row>
                <mat-row *matRowDef="let row; columns: ['expandedDetail'];"
                    [@detailExpand]="row.isExpanded == true ? 'expanded' : 'collapsed'" style="overflow: hidden">
                </mat-row>
            </mat-table>
            <mat-paginator #mainTablePaginator [pageSizeOptions]="[5, 10, 15]" showFirstLastButtons></mat-paginator>
        </div>
    </div>
</div>

In the typescript file, we have student data the same as the previous example, plus we have added data for marks as well. Next, we have assigned student data to the data source and whenever the user expands row, we have assigned marks data to the child table. Here we can make things dynamic based on our requirements like we can bind all data at first or on click we can make a call to the server and then bind response to child table and so on.
All the above is about data, now let’s talk about paginator. Paginator of outer table is assigned when we assigned data and it’s a one-time job so it’s easy. But when we assign paginator to the child table, it depends on our implementation when to assign a child’s paginator, in case of static data we can assign it at starting but if we are getting data on click event then we have to bind paginator at that time. You can understand it by looking at code.
Declaration of paginators are also different, in case of the outer table, we only have single paginator so we can access it by using @viewChild. But in case of child tables paginator, they are multiple, so we need it to access using @viewChildren and QueryList you can check in below code.

app.component.ts
import { ComponentViewChildOnInitViewChildrenQueryList } from '@angular/core';
import { triggerstatestyletransitionanimate } from '@angular/animations';
import { MatTableDataSourceMatPaginator } from '@angular/material';

export interface Student {
  rollNonumber;
  namestring;
  marksnumber;
  standardstring;
  isExpandedboolean;
}

const dataStudent[] = [
  { isExpanded: falserollNo: 1name: 'Ramesh'marks: 78standard: '10' },
  { isExpanded: falserollNo: 2name: 'Suresh'marks: 56standard: '12' },
  { isExpanded: falserollNo: 3name: 'Adi'marks: 77standard: '7' },
  { isExpanded: falserollNo: 4name: 'Rina'marks: 57standard: '9' },
  { isExpanded: falserollNo: 5name: 'Tapil'marks: 66standard: '9' },
  { isExpanded: falserollNo: 6name: 'Sugul'marks: 88standard: '5' },
  { isExpanded: falserollNo: 7name: 'Aftar'marks: 46standard: '5' },
  { isExpanded: falserollNo: 8name: 'Oxa'marks: 57standard: '5' },
  { isExpanded: falserollNo: 9name: 'Tam'marks: 76standard: '5' },
  { isExpanded: falserollNo: 10name: 'Luis'marks: 87standard: '7' }
];

export class Marks {
  teststring;
  marathinumber;
  englishnumber;
  mathsnumber;
}

const marksForPerticularStudentMarks[] = [
  { test: 'test 1'marathi: 60english: 65maths: 45 },
  { test: 'test 2'marathi: 67english: 40maths: 40 },
  { test: 'test 3'marathi: 57english: 66maths: 55 },
  { test: 'test 4'marathi: 76english: 68maths: 76 },
  { test: 'test 5'marathi: 76english: 68maths: 76 },
  { test: 'test 6'marathi: 76english: 68maths: 76 },
  { test: 'test 7'marathi: 76english: 68maths: 76 },
  { test: 'test 8'marathi: 76english: 68maths: 76 }
];

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [
    trigger('detailExpand', [
      state('collapsed'style({ height: '0px'minHeight: '0'visibility: 'hidden' })),
      state('expanded'style({ height: '*'visibility: 'visible' })),
      transition('expanded <=> collapsed'animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
    ]),
  ],
})

export class AppComponent implements OnInit {

  displayedColumns = ['expand''rollNo''name''marks''standard'];
  displayedColumnsSecondTable = ['test''marathi''english''maths'];

  dataSource = new MatTableDataSource<Student>();
  dataSourceSecondTable = [];

  @ViewChild('mainTablePaginator'mainTablePaginatorMatPaginator;
  @ViewChildren('subTablePaginator'subTablePaginator = new QueryList<MatPaginator>();

  constructor() { }

  ngOnInit(): void {
    this.dataSource.data = data;
    this.dataSource.paginator = this.mainTablePaginator;
  }

  expandCollapse(rowindex) {
    row.isExpanded = row.isExpanded === true ? false : true;
    this.dataSourceSecondTable[index] = new MatTableDataSource<Marks>();
    this.dataSourceSecondTable[index].data = marksForPerticularStudent;
    this.dataSourceSecondTable[index].paginator = this.subTablePaginator.toArray()[index];
  }

}


Small CSS code to make +, - button bold and large and little for table.

app.component.scss
.signs {
  font-weightbolder;
  font-sizelarge;
}

.innerTable {
  width100% !important;
  padding2px;
}

.cellColor {
  backgroundlightgrey;
}


Here are the sample screenshots of output.




That’s it, we have successfully implemented a demo for multiple Paginators for a child material table which are inside of expandable/collapsible rows in the angular material table. Hope you like it.

Download project here[click me]

Comments

Post a Comment