Arrange information in table format using Angular Material

I have successfully set up a component in Angular and Material. The data I need is accessible through the BitBucket status API:

However, I am facing an issue with enabling column sorting for all 3 columns using default settings. Any help or guidance on this matter would be highly appreciated.

For reference, here is the stackblitz link: https://stackblitz.com/edit/angular-gmdy9j

HTML:

<div class="example-table-container">

    <table mat-table [dataSource]="data"
           matSort matSortActive="name" matSortDirection="desc">


      <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Name</th>
        <td mat-cell *matCellDef="let row">{{row.name}}</td>
      </ng-container>


      <ng-container matColumnDef="status">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Status</th>
        <td mat-cell *matCellDef="let row">{{row.status}}</td>
      </ng-container>


      <ng-container matColumnDef="created_at">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>
          Created
        </th>
        <td mat-cell *matCellDef="let row">{{row.created_at | date}}</td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
    </table>

  </div>

TS:

import {HttpClient} from '@angular/common/http';
import {Component, ViewChild, AfterViewInit} from '@angular/core';
import {MatSort} from '@angular/material';
import {merge, Observable, of as observableOf} from 'rxjs';
import {catchError, map, startWith, switchMap} from 'rxjs/operators';

@Component({
  selector: 'app-headlines',
  templateUrl: './headlines.component.html',
  styleUrls: ['./headlines.component.scss']
})

export class HeadlinesComponent implements AfterViewInit {
  displayedColumns: string[] = ['name', 'status', 'created_at'];
  tableDatabase: TableHttpDatabase | null;
  data: BitBucketIssue[] = [];

  resultsLength = 0;
  isLoadingResults = true;


  @ViewChild(MatSort) sort: MatSort;

  constructor(private http: HttpClient) {}

  ngAfterViewInit() {
    this.tableDatabase = new TableHttpDatabase(this.http);

    merge(this.sort.sortChange)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.tableDatabase!.getRepoIssues(
            this.sort.active, this.sort.direction);
        }),
        map(data => {

          // Flip flag to show that loading has finished.
          this.isLoadingResults = false;
          this.resultsLength = data.incidents.length;
          console.log(data.incidents.length)
          console.log(data.incidents)
          return data.incidents;

        }),
        catchError(() => {
          this.isLoadingResults = false;
          return observableOf([]);
        })
      ).subscribe(data => this.data = data);
  }
}

export interface BitBucketApi {
  incidents: BitBucketIssue[];
}

export interface BitBucketIssue {
  name: string;
  status: string;
  created_at: number;
}

/** An example database that the data source uses to retrieve data for the table. */
export class TableHttpDatabase {
  constructor(private http: HttpClient) {}

  getRepoIssues(sort: string, order: string): Observable<BitBucketApi> {
    const href = 'https://bqlf8qjztdtr.statuspage.io/api/v2/incidents.json';
    const requestUrl =
        `${href}?q=&sort=${sort}&order=${order}`;

    return this.http.get<BitBucketApi>(requestUrl);
  }
}

For further reference, here is the stackblitz link: https://stackblitz.com/edit/angular-gmdy9j

Answer №1

It appears that the issue lies in not connecting the necessary MatTableDataSource to your fetched data. You must link your MatSort component to the sort property of the MatTableDataSource, and utilize the MatTableDataSource for displaying the data. Detailed instructions can be found in the official documentation here

ANGULAR CODE

import {HttpClient} from '@angular/common/http';
import {Component, ViewChild, AfterViewInit} from '@angular/core';
import {MatPaginator, MatSort, MatTableDataSource} from '@angular/material';
import {merge, Observable, of as observableOf} from 'rxjs';
import {catchError, map, startWith, switchMap} from 'rxjs/operators';

/**
 * @title Data Retrieval via HTTP for Table
 */
@Component({
  selector: 'table-http-example',
  styleUrls: ['table-http-example.css'],
  templateUrl: 'table-http-example.html',
})
export class TableHttpExample implements AfterViewInit {
  displayedColumns: string[] = ['name', 'status', 'created_at'];
  tableDatabase: TableHttpDatabase | null;
  data: BitBucketIssue[] = [];
  dataSource: MatTableDataSource<BitBucketIssue>;

  resultsLength = 0;
  isLoadingResults = true;


  @ViewChild(MatSort) sort: MatSort;

  constructor(private http: HttpClient) {}

  ngAfterViewInit() {
    this.tableDatabase = new TableHttpDatabase(this.http);

    merge(this.sort.sortChange)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.tableDatabase!.getRepoIssues(
            this.sort.active, this.sort.direction);
        }),
        map(data => {

          // Update flag to indicate loading completion.
          this.isLoadingResults = false;
          this.resultsLength = data.incidents.length;
          console.log(data.incidents.length)
          console.log(data.incidents)
          return data.incidents;

        }),
        catchError(() => {
          this.isLoadingResults = false;
          return observableOf([]);
        })
      ).subscribe(data => {
        this.data = data; this.dataSource = new MatTableDataSource(data);
        this.dataSource.sort = this.sort;
      });
  }
}

export interface BitBucketApi {
  incidents: BitBucketIssue[];
}

export interface BitBucketIssue {
  name: string;
  status: string;
  created_at: number;
}

/** A sample database used by the data source to obtain table data. */
export class TableHttpDatabase {
  constructor(private http: HttpClient) {}

  getRepoIssues(sort: string, order: string): Observable<BitBucketApi> {
    const href = 'https://bqlf8qjztdtr.statuspage.io/api/v2/incidents.json';
    const requestUrl =
        `${href}?q=&sort=${sort}&order=${order}`;

    return this.http.get<BitBucketApi>(requestUrl);
  }
}

HTML

<div class="example-table-container">

    <table mat-table [dataSource]="dataSource"
           matSort matSortActive="name" matSortDirection="desc">

      <!-- Title Column -->
      <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Name</th>
        <td mat-cell *matCellDef="let row">{{row.name}}</td>
      </ng-container>

      <!-- State Column -->
      <ng-container matColumnDef="status">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Status</th>
        <td mat-cell *matCellDef="let row">{{row.status}}</td>
      </ng-container>

      <!-- Created Column -->
      <ng-container matColumnDef="created_at">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>
          Created
        </th>
        <td mat-cell *matCellDef="let row">{{row.created_at | date}}</td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
    </table>

  </div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for adding a mat-error to a mat-input-field on-the-fly

To handle user input exceeding maxLength and dynamically add < mat-error > to the DOM in case of an error, I have implemented an attribute directive that enforces the character limit on input fields. This directive is used across multiple files in th ...

There is no index signature that accepts a parameter of type 'string' in the type '{ [key: string]: AbstractControl; }'

I'm currently tackling a challenge in my Angular project where I am creating a custom validator for a reactive form. However, I've encountered an error within the custom validators function that I am constructing. Below you will find the relevan ...

Steps to initiate a PDF file download when a button is clicked using Angular 6

I need assistance with downloading the .pdf file when clicking a button. Below is the code I am currently using: downloadMyFile() { const link = document.createElement('a'); link.setAttribute('target', '_blank&apos ...

Optimizing and scaling Firebase for improved performance

Is it possible to efficiently retrieve schedules from a database with thousands, or even millions, of records in the future? I am planning on storing schedules from healthcare professionals in a collection, but I am unsure if it is better to store them wi ...

Achieving TypeScript strictNullChecks compatibility with vanilla JavaScript functions that return undefined

In JavaScript, when an error occurs idiomatic JS code returns undefined. I converted this code to TypeScript and encountered a problem. function multiply(foo: number | undefined){ if (typeof foo !== "number"){ return; }; return 5 * foo; } ...

Using TypeScript with Vue to Retrieve Information from a Form

Currently, I am attempting to retrieve data from a form in Vue using TypeScript. However, when declaring the data that I intend to use with this form, it seems to be posted on the fields as shown in this screenshot: message getting posted. I am unsure ho ...

What is the method for implementing a custom layout for the items within a Select component?

I want to customize the options of a Select component by adding HTML elements. Here is an example: <mat-select [(ngModel)]="items"> <mat-option *ngFor="let item of ($items | async)" [value]="item.id"> <span>{{item.name}}</span&g ...

How to prevent Cut, Copy, and Paste actions in a textbox with Angular 2

I am currently utilizing Angular2 to prevent copying and pasting in a textbox. However, I am seeking guidance on how to create a custom directive that can easily be applied to all text fields. Here is the code snippet that successfully restricts the copy ...

Passing data from child to parent in Angular using EventEmitter

I have integrated a child grid component into my Angular application's parent component, and I am facing an issue with emitting data from the child to the parent. Despite using event emitter to transmit the value to the parent, the variable containing ...

The current directory does not belong to a Cordova project

Upon executing ionic cordova run browser --verbose within the main directory of my Ionic4 project, I encounter the error message "Current working directory is not a Cordova-based project." as shown below. I've observed that the command generates a "w ...

No interface 'JSX.IntrinsicElements' could be found, causing the JSX element to be implicitly of type 'any'

<Header> <title>Real Estate API Application</title> <meta name="description" content="Generated by create next app" /> <meta name="viewport" content="width=device-width, ...

The index declaration file has not been uploaded to NPM

After creating a Typescript package and publishing it on NPM, I encountered an issue with the declaration files not being included in the published version. Despite setting declaration: true in the tsconfig.json, only the JavaScript files were being publis ...

The error message "Declaration file for module 'mime' not found" was issued when trying to pnpm firebase app

Currently, I am in the process of transitioning from yarn to pnpm within my turborepo monorepo setup. However, I have run into an issue while executing lint or build commands: ../../node_modules/.pnpm/@<a href="/cdn-cgi/l/email-protection" class="__cf_e ...

What is the best way to find a match for {0} while still allowing for proper

I am working on developing a text templating system that allows for defining placeholders using {0}, similar to the functionality of .Net's string.format method. Here is an example of what I am aiming for: format("{0}", 42), // output ...

Issue with closing the active modal in Angular TypeScript

Can you help me fix the issue with closing the modal window? I have written a function, but the button does not respond when clicked. Close Button: <button type="submit" (click)="activeModal.close()" ...

Leveraging TypeScript for defining intricate tree manipulation guidelines

In my current project, I am working on enhancing a TypeScript process that is in place. The goal is to make it more strongly typed for better scalability and accuracy. The structure of the existing tree under consideration is as follows: interface Node { ...

Is there a method to prevent explicitly passing the context of "this"?

Currently, I am in the process of developing a new product and have set up both back-end and front-end projects. For the front-end, I opted to use Angular framework with Typescript. As a newcomer to this language (just a few days old), I have encountered a ...

The "number" input type is functioning correctly on Chrome, but on Firefox, it is accepting characters other than numbers

While developing in Angular, I encountered a challenge where I needed to create an input that only accepts numeric characters. Initially, I tried using type="number" which worked perfectly in Google Chrome but surprisingly allowed non-numeric characters ...

Uh oh! The form submission has caused an error of type TypeError, stating that it is unable to read the property "

Upon submitting a form, the console displays this error: ERROR TypeError: Cannot read property 'text' of undefined The submit method is being passed to a component with an output decorator. Displayed below is the code : comments.component.t ...

Getting started with html2canvas: A beginner's guide

So here's a seemingly simple question... I'm diving into new territory and stumbled upon http://html2canvas.hertzen.com with a straightforward tutorial. After successfully running the command npm install -g html2canvas, I hit a roadblock. Where e ...