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

Error: Attempting to add types to an object returned from the .map function in JSX Element

When attempting to include the item type in the object returned from the .map function, I encountered a JSX error. I tried specifying item: JSX.Element as the Item type, but this didn't resolve the issue. Can someone provide clarity on this matter? Th ...

Challenges with Nested Reactive Form in Angular 4

I am utilizing Angular 4 reactive to develop nested forms. The structure of my forms is as follows: Form Group (userform) FormArray (users) FormGroup (idx) In the complete table, it falls under the Form Group. Each table row is a part of FormArray ...

Encountering a 500 Error in an Angular 6 application on a .NET Core 2.1.1 framework when deployed

After upgrading my project from the old Visual Studio SPA Angular template to the latest version, including moving from Angular 5 to Angular 6 and webpack to angular-cli, I encountered an issue. While everything works fine in development, I'm facing a ...

Tips for specifying a variable as a mandatory key attribute within an array

Is there a way to dynamically determine the type of key attribute in an array? const arr = [ { key: 'a' }, { key: 'b' }, { key: 'c' }, ]; type key = ??? // Possible values for key are 'a', 'b', or &a ...

Event emission is not working in the Ionic directive

I've developed a custom Ionic 5 Directive for long press functionality. Take a look at the code snippet below: export class LongPressDirective implements AfterViewInit { private delay = 800; @Output() press = new EventEmitter(); action: any; ...

Nested validation schema featuring conditional validation - yes, we've got it covered!

In my Formik object, I have set initial values as follows: {customerDetails: {id: "", name: "", mobileNumber: ""}, notes: {id: "", text: "", type: ""}} How can I create a conditional Yup validati ...

The type 'function that takes in a CustomEvent and returns void' cannot be assigned to a parameter of type 'EventListenerOrEventListenerObject'

When I upgraded from TypeScript version 2.5.3 to 2.6.1, my custom event setup started giving me an error. window.addEventListener('OnRewards', (e: CustomEvent) => { // my code here }) [ts] Argument of type '(e: CustomEvent) => vo ...

Styling child elements in Angular using css from its parent element

I have a question: Regarding the structure below <component-parent> <first-child> <second-child> <third-child></third-child> </second-child> </first-child> </component-parent> Now I want t ...

How to calculate the sum of all values in a FormArray in Angular

I am trying to retrieve the input values from each row and then calculate the sum of these rows. Here is my HTML code: <ng-container formArrayName="cap_values"> <tbody *ngFor="let item of capValues.controls; let i=index" [formGroupName]="i"& ...

Tips for ensuring your controls function properly and seamlessly when switching to another page

I utilized the instructions from this post to implement a slider. However, I encountered an issue with the controller when navigating to subsequent pages. While the controller functions correctly on the initial page, it duplicates the same values on the fo ...

Tips for fixing TypeScript compiler error TS2339: Issue with accessing 'errorValue' property in Angular 5 project

Within a component, I have developed a function to manage errors returned from a Rest Service and determine the corresponding error message to display to the user. This method accepts an error object (custom data structure from the service), navigates to e ...

What is the syntax for declaring a state variable as a Set data type in programming?

Struggling to establish a state variable in React using Typescript. Encountering an error when attempting to specify its type as Set. The purpose of this variable is to contain an array of objects. const [blocksList, setBlocksList] = useState<Set>([ ...

Angular 17 isn't notifying child component of signal changes

In the statistics module, I have a signal that specifies the type of charts to display and this signal is updated through a radio button group. The signal: typeSignal = signal<string>('OIA') The radio buttons for setting the : <div clas ...

The TypeScript compiler throws an error when encountering nulls in conjunction with the isNull function

Whenever I set strictNullChecks: true in tsconfig.json and utilize the isNull function for null checks, the compiler throws the error TS2531: Object is possibly 'null'. Interestingly, isNull doesn't trigger any errors in VsCode, however, the ...

React.js - Issue with table not being redrawn after data filtering

I am encountering an issue with my table in Next.js where the text input is not triggering a redraw. The expected behavior is to update the table with a single search result, but currently only the top row reflects the search result. Below is my useEffect ...

Unveiling typescript property guards for the unknown data type

Is there a way to type guard an unknown type in TypeScript? const foo = (obj: unknown) => { if (typeof obj === 'object' && obj) { if ('foo' in obj && typeof obj.foo === 'string') { r ...

Having difficulty authenticating a JWT token in my Nextjs application

Help required with verifying a JWT token in Nextjs as I'm encountering the following error: TypeError: Right-hand side of 'instanceof' is not an object See below for the code I am currently using: useEffect(() => { let token = localS ...

Is there a way to invoke an Angular2 function from within a Google Map infowindow?

I am currently working on integrating Google Maps using javascript in a project, and I'm facing a challenge. I want to call an Angular2 function inside an infowindow as shown in the code snippet below. Pay attention to the infoContent variable that co ...

The karma test encounters difficulties in establishing a connection with the 'chrome' instance that has been launched

Currently, I am facing an issue with my Karma test running on a nodejs jenkins pod. npm is timing out when trying to connect to the Chrome instance on the selenium hub. Everything was working fine until yesterday without any changes made to the configura ...

What is the method for determining the width of a Mat-Table once it has been displayed?

When utilizing Angular Material Mat-Table in conjunction with Angular 8, I am passing the dataSource dynamically. The number of rows and columns varies each time. Is there a method to calculate the width of the table once it is rendered on the screen? &l ...