Skip to content

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.

Only Added Content
<!-- 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>

Only Added Content
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
<!-- 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 .

Comments