Table
In the following examples, we will be using the GitHub api to perform searches for repos. I chose this api because it supports pagination and I wanted to demonstrate using pagination to trigger api calls.
Mat Table with Api¶
We're using the dynamic column approach with the columns defined in table-example.component.ts
.
<mat-form-field class="example-form-field" appearance="fill">
<mat-label>Search all GitHub Repositories</mat-label>
<input matInput type="text" [(ngModel)]="query" (keydown.enter)="search()" />
<button matSuffix mat-icon-button aria-label="Clear" (click)="search()">
<mat-icon>search</mat-icon>
</button>
</mat-form-field>
@switch(loading) {
<!-- Show Loading Spinner -->
@case(true) {
<mat-spinner
[style.position]="'absolute'"
[style.top]="'50%'"
[style.left]="'50%'"
></mat-spinner>
}
<!-- Show Table -->
@case(false) {
<div class="table-container">
<table
mat-table
[dataSource]="searchResult.items"
class="mat-elevation-z8 demo-table">
@for (column of columns; track column) {
<ng-container [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef>
{{ column.header }}
</th>
<td mat-cell *matCellDef="let row">
{{ column.cell(row) }}
</td>
</ng-container>
}
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
}
}
@Component({
selector: 'app-table-example',
templateUrl: './table-example.component.html',
styleUrls: ['./table-example.component.css'],
standalone: true,
imports: [
MatTableModule,
FormsModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatProgressSpinnerModule,
NgIf,
],
})
export class TableExampleComponent implements OnInit {
query: string = '';
loading: boolean = false;
searchResult: GitHubSearchResult = {
items: [],
};
columns = [
{
columnDef: 'full_name',
header: 'Name',
cell: (element: GitHub) => `${element.full_name}`,
},
{
columnDef: 'html_url',
header: 'URL',
cell: (element: GitHub) => `${element.html_url}`,
},
{
columnDef: 'license.name',
header: 'License',
cell: (element: GitHub) => `${element.license?.name}`,
},
{
columnDef: 'watchers_count',
header: 'Watchers',
cell: (element: GitHub) => `${element.watchers_count}`,
},
];
displayedColumns = this.columns.map((c) => c.columnDef);
constructor(private gitHubApiService: GitHubApiService) {}
ngOnInit() {}
search(): void {
this.loading = true;
this.gitHubApiService
.get(this.query, 0, 10)
.pipe(take(1))
.subscribe({
next: (data) => {
this.searchResult = data;
},
complete: () => {
this.loading = false;
},
});
}
}
const baseUrl = 'https://api.github.com/search/repositories';
@Injectable({
providedIn: 'root',
})
export class GitHubApiService {
constructor(private http: HttpClient) {}
get(
query: string,
page: number = 0,
per_page: number = 5
): Observable<GitHubSearchResult> {
const params = new HttpParams()
.set('q', query)
.set('page', page)
.set('per_page', per_page);
return this.http.get<GitHubSearchResult>(baseUrl, { params: params });
}
}
If the stack blitz has trouble loading, try opening it in a new tab .
Pagination¶
In the prior example, we hard coded a page value of 0
and a page size of 10
. Now let's give those options to the user.
<!-- Adding mat-paginator under table -->
<mat-paginator
*ngIf="showPaginator"
[length]="resultsLength"
[pageSize]="pageSize"
[pageSizeOptions]="pageSizeOptions"
[pageIndex]="pageIndex"
showFirstLastButtons="true"
aria-label="Select page"
(page)="handlePageEvent($event)">
</mat-paginator>
export class TableExampleComponent implements OnInit {
// Initial Content
resultsLength: number = 0;
pageSizeOptions: number[] = [5, 25, 50];
pageIndex?: number = 0;
pageSize?: number = 5;
search(): void {
this.loading = true;
this.gitHubApiService
.get(this.query, this.pageIndex, this.pageSize)
.pipe(take(1))
.subscribe({
next: (data) => {
this.searchResult = data;
this.resultsLength = data.total_count || 0;
},
complete: () => {
this.loading = false;
},
});
}
public handlePageEvent(event?: PageEvent): void {
this.pageIndex = event?.pageIndex;
this.pageSize = event?.pageSize;
this.search();
}
get showPaginator(): boolean {
return this.searchResult.items.length > 0 && !this.loading;
}
Feel free to see this particular example in StackBlitz . Otherwise, it will effectively be incorporated in the next demo.
Dynamic Cards or Table¶
Just to spice things up, let's add a toggle button which will toggle between a mat-card view and a mat-table view. This is a great example that shows us how to either us cards within a for loop, or simply a table.
<!-- Only added content -->
<mat-button-toggle-group
#toggleButton="matButtonToggleGroup"
name="view"
aria-label="View"
value="card"
>
<mat-button-toggle value="card">Cards</mat-button-toggle>
<mat-button-toggle value="table">Table</mat-button-toggle>
</mat-button-toggle-group>
@if (!loading && toggleButton.value === 'card'){
<!-- -->
@for (github of searchResult.items; track github) {
<mat-card>
<mat-card-header>
<mat-card-title>{{ github?.full_name }}</mat-card-title>
<mat-card-subtitle>{{ github?.full_name }}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>
<b>URL:</b> {{ github?.html_url }}
<br />
<b>License:</b> {{ github?.license?.name }}
<br />
<b>Watchers:</b> {{ github?.watchers_count }}
<br />
</p>
</mat-card-content>
</mat-card>
}
<!-- -->
}
If the stack blitz has trouble loading, try opening it in a new tab .