Dealing with numerous dynamically generated tables while incorporating sorting in Angular: a comprehensive guide

I am faced with a challenge involving multiple dynamically created Angular tables, each containing the same columns but different data. The issue at hand is sorting the columns in each table separately. At present, I have two tables set up. On clicking the column header arrow on the first table, it sorts correctly while doing the same on the second table yields no result.

Below is the pertinent HTML code snippet:

  <div appMaterialElevation *ngFor="let item of tables; let i = index">  
    <table
      mat-table
      [dataSource]="item.dataSource"
      multiTemplateDataRows
      matSort
      matSortActive="configName"
      matSortDirection="asc"
    > 
      <ng-container matColumnDef="configName">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Table Name</th>
      </ng-container>

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

Here's the relevant TypeScript snippet:

import { Component, ViewChild, ViewChildren, QueryList, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
export class Row {
  configName: string;
}

export class FormatData {
  formatName: string;
  dataSource: MatTableDataSource<Row>;
  selection: SelectionModel<Row>;
}

export class ConfigureFormatsComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChildren(MatPaginator) paginators = new QueryList<MatPaginator>();
  @ViewChildren(MatSort) sorts = new QueryList<MatSort>();
  tables: FormatData[];
  displayedColumns: string[] = ['configName'];

  getconfigformats() {
    this.tables = [] as FormatData[];    
    this.myService.getMyData()
      .subscribe((configList: MyConfigs[]) => {
        let table = new FormatData();
        let configNamesList = [] as Row[];
        configList.forEach(config => {
          let row = new Row();
          row.configName = config.configName;
          configNamesList.push(row);
         });
       table.dataSource = new MatTableDataSource<Row>(configNamesList);
       table.selection = new SelectionModel<Row>(true, []);
       this.tables.push(table);
       this.ngAfterViewInit();
     }
   });
  }

  ngAfterViewInit() {
    for (let i=0; i<this.tables.length; i++) {
      let table = this.tables[i];
      table.dataSource.sort = this.sorts.toArray()[i];
      table.dataSource.paginator = this.paginators.toArray()[i];
    };
  }

Is there anyone who can identify what might be causing my issue?

Answer №1

It is important to assign the sort to each "dataSource"

Therefore, a solution could look like this:

this.tables.forEach((item,i)=>{
  item.dataSource.sort=this.sort.find((_,index)=>index==i)
}

Notice how we select the QueryList using find((_x,index)

NOTE: it's crucial to note that

   //note that there are no ()
  @ViewChildren(MatPaginator) paginators = new QueryList<MatPaginator>;
  @ViewChildren(MatSort) sorts = new QueryList<MatSort>;

Update: It is necessary to allow Angular time to render the tables. We can achieve this by enclosing it in a setTimeout when assigning the MatSort.

In the stackblitz example, I have used something similar to:

dataSources:MatTableDataSource<any>[]=[]
@ViewChildren(MatSort) sorts:QueryList<MatSort>

ngOnInit()
{
  service.getData().subscribe((res:any[])=>{
    res.forEach((x,index)=>{
         this.dataSources[index]=new MatTableDataSource(x)
     })
     setTimeout(()=>{
      this.dataSources.forEach((x,index)=>{
        x.sort=this.sorts.find((_,i)=>i==index)
      })
     })
    })
  }

Update2: If you are unsure about your code, you can create a function like:

setSorts()
{
   setTimeout(()=>{
      this.tables.forEach((item:any,index:number)=>{
        const sort=this.sorts.find((_,i:number)=>i==index)
        item.dataSource.sort=sort
      })
   })
}

NOTE: Please note that your dataSource should be a MatTableDataSource. If you have an array, you should use:

table.dataSource=new MatTableDataSource<Row>(yourArray);

The setTimeout is necessary because when you add an element to your array, there may not be any sorting applied initially. You need to "wait" for Angular to render the tables. The setTimeout tells Angular: "hey! render the tables and then execute the functions inside the setTimeout."

Always remember to call this function whenever you add or remove an element from the "this.tables" array (each time you do a this.tables.push(..) or this.tables.remove)

About using find((_x,index)=>....), arrays or queryLists have methods like forEach, map, find.. which we usually use with only one argument, e.g. `

myArray.forEach((x:any)=>{...x is the value of the element..}

However, these methods also allow a second argument representing the index of the element, e.g.

myArray.forEach((x:any,index:number)=>{
 ...x is the value of the element and index the position..
 console.log(x,index)
}

When using "find", you can retrieve the element at the specified 'index'. Since we don't necessarily need the value of the variable, using a _ is common practice (but you can use any variable name)

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

Steps to set up Node.js Express.js for API, React.js for the front end, and Angular for the admin panel

Is there a way to deploy nodejs with SSL certificates? I am using expressjs for API, reactjs for front-end, and angular for backend. I need specific paths like for frontend, for admin, and the expressjs API running in the background with https, similar t ...

How to build a login page with a static header and footer using Angular2

For my latest project, I am currently in the process of developing an application using Angular2 and eclipse Neon. Utilizing angular-cli for this app, I am now focused on creating the login page. Within the app.component.html file, you will find the follow ...

Issue with pre-selected default value in AngularJS TypeScript Kendo UI DropDownList

I have successfully implemented a list of objects for items, but now I am facing a challenge in adding a default selected value. Below is the HTML code for the drop-down: <select kendo-drop-down-list k-options="selectItems" k-ng-mode ...

Encountering a Typescript error when defining a curried function after an uncurried function

Upon placing the uncurried definition of a method above the curried definition, I encountered an error stating: Expected 1 arguments, but got 2.ts(2554). The dtslint test that failed reads as follows: function match(regExpression: RegExp, str: string): st ...

"Launching" conduit for Observable

Is there a way to attach two pipes to an HttpClient request in order to execute functions at the beginning and end of the request? I have discovered the "finalize" operator for executing a function when the request is finished, but I am looking for an equi ...

Securing your Angular application with an SSL certificate and key in the Ng Serve root directory

I am currently attempting to configure a SSL key and certificate on my ng serve using the following command: ng serve --ssl true --ssl-key './assets/somekey.key' --ssl-cert './assets/somecert.cert' However, when I run this command, th ...

Navigating the Angular Element: A Guide to Clicking Buttons within Modal-Dialogs Using Protractor

I am currently creating an automation test for an angular application using the protractor framework. Test scenario: Click on the "Create PDF Report" button A modal-dialog window will appear Click on the "Run Report Now" button within the modal-d ...

Issue with using useState inside alert: unexpected empty array

I am facing an issue with the 'exercises' useState in my code. The useEffect function correctly prints the 'exercises' after every change, but when I press the 'Finish' button, the 'exercises' suddenly become empty. ...

Consolidating Angular 4 Observable HTTP requests into a single Observable to optimize caching

I am currently working on an Angular 4 application that serves as a dashboard for a system. Several different components within the application make calls to the same REST endpoint using identical TypeScript service classes. While this setup functions corr ...

Dealing with reactive form controls using HTML select elements

I am working with a template that looks like this: <form [formGroup]="form"> <mdl-textfield type="text" #userFirstName name="lastName" label="{{'FIRSTNAME' | translate}}" pattern="[A-Z,a-zéè]*" error-msg ...

When attempting to install an npm package using npm link, you may encounter the error TS2307: Module not found

I'm in the process of developing an Angular library called clan-auth that will contain shared components for multiple Angular projects. When I install the library from our private npm Repository, everything works as expected. However, when I try to li ...

Executing debounceTime outside of Angular's zone inside a material dialog

I encountered an issue while attempting to filter a list of objects within a mat-dialog popup window. My approach was inspired by this helpful post which suggested using debouncing to optimize Angular change detection on keyUp events. Upon implementing th ...

Unusual class title following npm packaging

Currently, I am working on developing a Vue 3 library with TypeScript. We are using Rollup for bundling the library. Everything works as expected within the library itself. However, after packing and installing it in another application, we noticed that th ...

Efficient method to activate openLayers 3 'singleClick' event from karma test

We are incorporating angular2 and openlayers3 to showcase a map on our webpage. The singleClick feature is utilized to exhibit a popup when the marker on the map is clicked - this.map.on('singleclick', (e) => { this.showW ...

Angular 2's innovative approach to implementing a sticky footer concept

Is there a way to make my footer sticky without being fixed? I attempted using the CSS negative margin trick, but it did not work as expected. In the provided Plunker code, I tried to replicate the issue in my Angular 2 app. The goal is for the footer to s ...

Using LitElement: What is the best way to call functions when the Template is defined as a const?

When the template is defined in a separate file, it's not possible to call a function in the component. However, if the template is defined directly as returning rendered HTML with this.func, it works. How can one call a function when the template is ...

Creating a hierarchical visualization in Angular using a JSON object array

I'm currently working on creating a hierarchical view of users. My ultimate goal is to utilize this hierarchy view or something similar. The challenge lies in how the JSON objects used to construct the hierarchy are structured. Here's an example ...

Transform the Standard class into a generic one in typescript

I've created a class that can take JSON objects and transform them into the desired class. Here's the code: import {plainToClass} from "class-transformer"; import UserDto from "../../auth/dto/user.dto"; class JsonConverter { ...

The modal stubbornly refuses to close

The main component responsible for initiating the process is /new-order. Upon clicking on the confirm button, a modal window appears. <div class="col-12"> <button type="button" class="btn btn-primary m-1" (click)=& ...

Receiving an error message "Cannot read property 'X' of undefined" when invoking a sibling method

I'm completely new to Angular and trying to understand how events work within this framework. Here is my Parent Template setup: <child1 (myEvent)="child2.testMethod()"></child1> <child2 #child2 *ngIf="show"></child2> When I ...