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

Developing a TypeScript library for versatile features across multiple projects

My goal is to export multiple classes, some independent and others interdependent, encapsulated within a single namespace, in the form of a module for external project utilization. To achieve this, I have configured a webpack build to compile these classe ...

The process of retrieving input field values from a table within an Angular reactive form

I am seeking a way to collect all input values found within the table rows inside a reactive form. Below is an explanation of my code. <form [formGroup]="productForm" (ngSubmit)="saveProduct($event)" novalidate> <mat-form-field appearance="outlin ...

Issue with Angular polyfill in IE11: The core-js version 3.6.5 method es.string.split.js is having trouble parsing the regex /^|s+/ when used with

Angular 10, d3 5.16.0, and core-js 3.6.5 In the midst of it all, d3-drag triggers d3-dispatch, which in turn invokes a function called .parseTypenames. function parseTypenames(typenames, types) { return typenames.trim().split(/^|\s+/).map(functio ...

How can I display 4 react components, such as custom buttons, in a manner that ensures the most recently pressed button appears on top?

I've been attempting to solve this problem, but I'm struggling to find a solution. My current approach involves grouping the 4 button components in an array and shifting their positions based on user input. Is there a more efficient way to accomp ...

Ensuring the accuracy of nested objects through class validator in combination with nestjs

I'm currently facing an issue with validating nested objects using class-validator and NestJS. I attempted to follow this thread, where I utilized the @Type decorator from class-transform but unfortunately, it did not work as expected. Here is my setu ...

Best practices for customizing Material UI v5 Switch using Theme

I've successfully implemented a Material Switch based on my design by creating a custom component and styling it using the styled and sx prop. However, I'm interested in figuring out how to achieve the same result within the theme itself so that ...

To implement a filter in MongoDB, make sure to specify a function argument before

Utilizing TypeScript, Node.js, Mongoose, and MongoDB in my project. I have a function that resembles the following: async function getAllBooks(title?: string, authorName?: string, sortBy?) { const books = await bookModel.find().sort(); return book ...

Retrieving an array of objects from a JSON file using Angular 2

I am encountering an issue where the class is not filled properly in an object obtained from a JSON array, resulting in an 'undefined' error. Here is the code snippet for retrieving the object: getFeatures() { return this.http.get('h ...

Troubleshooting: Unable to locate .vue.d.ts file during declaration generation with Vue, webpack, and TypeScript

Currently, I am developing a library using Typescript and VueJS with webpack for handling the build process. One of the challenges I'm facing is related to the generation of TypeScript declaration files (.d.ts). In my source code, I have Typescript ...

Accessing the 'comment' property within the .then() function is not possible if it is undefined

Why does obj[i] become undefined inside the .then() function? obj = [{'id': 1, 'name': 'john', 'age': '22', 'group': 'grA'}, {'id': 2, 'name': 'mike', &apo ...

Developing a React-based UI library that combines both client-side and server-side components: A step-by-step

I'm working on developing a library that will export both server components and client components. The goal is to have it compatible with the Next.js app router, but I've run into a problem. It seems like when I build the library, the client comp ...

Using Typescript: invoking static functions within a constructor

This is an illustration of my class containing the relevant methods. class Example { constructor(info) { // calling validateInfo(info) } static validateInfo(info):void { // validation of info } I aim to invoke validateInfo ...

How should we correctly import jquery.inputmask?

Struggling to import jquery.inputmask using webpack and TypeScript? Head over to this discussion at Issue #1115 : Here's how I configured things with jqlite: To import in your app, use the following code: import InputMask from 'inputmask&apos ...

Angular (Ionic): Updating component view with loop does not refresh

When utilizing *ngFor to repeat a component multiple times, the components displayed are not updating in the DOM as expected. Displaying the component within an ionic page: <ion-row *ngFor="let file of files; let i = index"> <app-add ...

The interaction between Web API, Angular, and the unique MAC Address

My organization currently operates an Application using ASP MVC. However, the application is experiencing slow performance and confusion due to multiple programmers with conflicting ideas. I have taken on the task of refactoring and migrating it to Angular ...

Attempting to send numerous identifiers in an API request

I encountered a problem while working on a function in Angular that involves pulling data from an API. My goal is to enhance a current segment to accommodate multiple IDs, but I face difficulties when attempting to retrieve more than one ID for the API que ...

Migrating to Angular Universal for server-side rendering with an external API server

Thank you for taking the time to read my query. I have developed a project using server-side Node.js and client-side Angular 6. The purpose of the app is to provide information on cryptocurrency prices and details. I am now looking to transition my Angu ...

Issue encountered: [object Object] is being displayed upon attempting to submit my username and password through a REST service in Angular

My code was written in Angular CLI using the Sublime text editor and Node.js. The login component can be found in logincomponent.ts file. import { Component, OnInit } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms ...

Exploring the concept of individuality within front-end development

Would it be possible to manage identity directly on the front end using Angular's auth guard instead of setting up identity management on the server-side? The auth guard could handle all aspects of identity, even for smaller projects. For instance, in ...