Implement Sorting Functionality in Angular Using FormArray

Hello, I am a beginner in Angular and need some help with implementing sorting functionality. I have an input class called Foo which contains a list of books with properties like Id, Title, and Description. These books are displayed in a table where users can add, edit, or delete them. The feature to add and delete books is working correctly.

However, when I tried to add sorting using MatSort and Sort from Angular itself, it didn't work as expected. I'm not sure what I'm doing wrong. Should I switch to using MatTable instead of looping through a form array to achieve sorting? And if yes, how can I implement this with inputs instead of displaying data variables like {{element.title}}?

I appreciate any assistance on this matter.

Thank you!

@Input() foo: Foo;
@ViewChild(MatSort, {static: true}) sort: MatSort;
bookForm: FormArray;
orderForm: FormGroup;
bookList !: Book[];
bookSorted : Book[];
initForm() {
  this.orderForm= this._formBuilder.group( {
    customerForm: this._formBuilder.array( [] ),
    bookForm: this._formBuilder.array( [] )
  } );

  this.addedBooks()
  this.bookList = this.foo.Books;
}

addedBooks() {
  this.bookForm= this.orderForm.get( 'bookForm' ) as FormArray;
  this.bookForm.clear();
  let _bookForm = this.foo.books?.map( _book => this.addBook( _book ) );
  _bookForm?.forEach( _addBook => this.bookForm.push( _addBook ) );

}

addBook( _book) {
  return this._formBuilder.group( {
    title: new FormControl( _book?.title),
    description: new FormControl( _book?.description ),
    id: new FormControl( _book?.id ?? Guid.EMPTY ),
  } );
}

get bookFormControls {
  return ( this.orderForm.get( 'bookForm' ) as FormArray ).controls;
}

sortBook(sort: Sort) {
  const data = this.bookList.slice();
  if (!sort.active || sort.direction == '') {
    this.bookSorted = data;
    return;
  }

  this.bookSorted = data.sort((a, b) => {
    let isAsc = sort.direction == 'asc';
    switch (sort.active) {
      case 'title': return this.compare(a.title, b.title, isAsc);
      case 'description': return this.compare(+a.description, +b.description, isAsc);
      default: return 0;
    }
  });
}

compare(a, b, isAsc) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

removeBooksAt( index ) {
  this.dialogName = "Book"
  this.modalRef = this.dialog.open( this.deleteBook, {
    width: '600px',
  } );
  this.modalRef.afterClosed().subscribe( res => {
    if ( res ) this.bookForm.removeAt( index );
  } );

}

addNewBook() {
  let formValue = this.orderForm.controls['bookForm'] as FormArray;
  formValue.status == 'VALID' ? this.createBooksForm() : this.showToast();
}

createBooksForm(data?: any) {
  this.booksForm = this.orderForm.get( 'booksForm ' ) as FormArray;
  this.booksForm .push( this.addBooksControls(data) );
}

addBooksControls(data?: any): FormGroup {
  return this._formBuilder.group( {
    title: [data?.title ??'', Validators.required],
    description: [data?.description ??'', Validators.required],
    id: [data?.id ??Guid.EMPTY]
  } );
}

HTML

<!--Mat Sort Test-->
<fieldset>
  <div>
    <legend>Books</legend>
    <table matSort (matSortChange)="sortBook($event)" class="card-table">
      <thead class="primary-color">
      <tr>
        <th mat-sort-header="title">
          Book Title
        </th>
        <th mat-sort-header="description">
          Description
        </th>
        <th class="colums-name">
          Actions
        </th>
      </tr>
      </thead>
      <tbody>
      <tr class="margin-1" formArrayName="bookForm"
          *ngFor="let group of bookFormControls; let _i = index;">
        <td [formGroupName]="_i">
          <input type="text" formControlName="title" class="margin-1 readonly" placeholder="Add title">
        </td>
        <td [formGroupName]="_i">
          <input type="text" formControlName="description" class="margin-1 readonly"
                 placeholder="Add description">
          <input type="hidden" formControlName="id">
        </td>
        <td style="text-align: center;">
          <i (click)="removeBooksAt(_i, 'Title')" class="fa fa-trash margin-right-mini"
             style="color:darkgrey; font-size: xx-large;;" aria-hidden="true"></i>
        </td>
      </tr>
      </tbody>
    </table>
  </div>
</fieldset>

Answer №1

Here's a summary of the changes that have been made:

  1. I added [formGroup] at the top of the table, but you may not need it if you already have it elsewhere in your code.

  2. Moved the formArrayName to the tbody element to be the parent of the *ngFor

  3. Transferred the [formGroupName] to the *ngFor line to ensure it is the parent of the form elements

  4. Ensure that you've imported MatSortModule into the child component

  5. Make sure you included provideAnimations() in the providers array of bootstrapApplication

  6. Note that using both Books and books interchangeably is incorrect; I standardized them all as books

  7. The main issue was sorting the data instead of the form controls; make sure to use the same formGroup array for sorting as well.

Changes to the sort function:

  Updated sortBook(sort: Sort) {
    // Function content here
  }

Please review the full code below and check for any doubts or missing points:

FULL CODE:

CHILD TS

 Updated TypeScript code

CHILD HTML

 Updated HTML code with necessary changes

PARENT

 Updated Parent Component 

Access the updated Stackblitz Demo here

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

Exploring the functionalities of custom modules in AngularJs

Is there a way for me to get started with this particular module over at ? Here's the controller I currently have: app.controller('FormController', function ($scope, localStorageService) { }); I've also injected localStorageService ...

a single line within a compartment

I am encountering an issue with the code snippet below, which is responsible for generating the location of different records within a table: return ( <TableRow> <TableCell > // some code_1 < ...

Issue with mediaelement in Angular 8: video playback does not display after 2 seconds

I'm encountering an issue with MediaElement js when trying to play videos in .m3u8 format within a slider containing multiple videos. Whenever I click on the play button for any video, only a 2-second clip is shown before the video disappears. Any as ...

What's the process for setting a value in selectize.js using Angular programmatically?

Currently, I am implementing the AngularJS directive to utilize selectize.js from this source: https://github.com/kbanman/selectize-ng In my scenario, I have two dropdowns and my goal is to dynamically populate one of them called selectizeVat based on the ...

NestJS testing issue encountered: Compiled JS file not found in E2E test using Mocha

I'm currently facing an issue with executing an E2E test. The file structure for the E2E test is auto-generated by nestcli. import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; i ...

The jsPdf library performs well on medium-sized screens like laptops, but when downloaded from larger monitor screens, the PDF files do not appear properly aligned

In the code snippet below, I am using html2canvas to convert HTML to PDF. The PDF download works fine on medium screens, but when viewed on larger screens, some pages appear blank and the content order gets shuffled. How can I resolve this issue so that th ...

Can you explain the significance of tslint's message: "Warning: The 'no-use-before-declare' rule necessitates type information"?

Can someone explain the significance of tslint's "no-use-before-declare" rule warning, which states that it requires type information? I've tried researching online but still don't fully understand its implications. ...

What are some alternative ways to create a text field with the same style as Material UI's without relying on the Material UI

With the power of HTML, CSS, and Reactjs I am aiming to create a functionality where the placeholder animates up to the border upon clicking on the text field An example can be seen with Material UI text field: https://material-ui.com/components/text-fie ...

What is the best way to incorporate an automatic scroll to the following section on a single page website

My goal is to implement a feature that enables automatic scrolling between sections as the user scrolls up or down. The smooth transition should occur when the user reaches halfway through each section, seamlessly moving to the next screen on the same page ...

Enhance the annotation of JS types for arguments with default values

Currently, I am working within a code base that predominantly uses JS files, rather than TS. However, I have decided to incorporate tsc for type validation. In TypeScript, one method of inferring types for arguments is based on default values. For example ...

What is the best way to show all cards in Angular when no filtering input is provided?

I have implemented a filter feature for multiple cards in Angular using Pipes. The filter works well, but I am facing an issue where no cards are displayed when there is no input value provided. I would like all the cards to be displayed when no input is g ...

how to retrieve an ID from a service using a promise within a controller

Hello everyone, I am currently facing an issue with retrieving the '_id' from a service. Here is my service: application.service('Arrears', [ function() { var db = new PouchDB('localhost:5984/arrears&a ...

Angular: The type AbstractControl<any> cannot be assigned to type FormControl

I am working with a child component that includes an input tag <input [formControl]="control"> component.ts file @Input() control: FormControl; In the parent component, I am using it as follows: <app-input [control]="f['email ...

Receiving a SyntaxError in Node.js with the message "Unexpected token *" while attempting to import

node: v10.16.3 npm: 6.12.0 Encountered an error while trying to import express in node. Referencing the code from https://github.com/angular-university/rxjs-course, specifically server/server.ts. To run server.ts, used the following command: $ ts-node ...

Generate a Table Using JSON Data with AngularJS and ng-repeat

I have some JSON data that looks like this: { "Workout1": { "Name": "First", "Rounds": [ { "Exercises": [ { "Name": "Exercise1", "Repeat": 10 }, { "Name": "Exercise2 ...

Align watermark content to the far left side

Having trouble getting my watermark to align properly on the left side of my website's main content. Here is how it currently looks: The issue arises when I resize the screen or switch to mobile view, as the watermark ends up covering the main conten ...

Visual Studio Code unable to locate source maps for typescript debugging

Looking for some help debugging a basic Hello World TypeScript file. Whenever I try to set a breakpoint, it seems like VS Code is having trouble locating the source map, even though it's saved in the same directory. I'm using Chrome as my browser ...

having difficulty iterating through the data using map function

When attempting to use the map function in React Material UI to display an array of data in a select box, I encountered the error TypeError: Cannot read properties of undefined (reading 'map') while using the following code. Can anyone help me id ...

What is the best way to set a fixed width for my HTML elements?

I am facing an issue with my user registration form where error messages are causing all elements to become wider when they fail validation. I need help in preventing this widening effect. How can I achieve that? The expansion seems to be triggered by the ...

Condition in Angular Schema Form

I'm currently utilizing Angular Schema Form to dynamically generate a form based on JSON Schema. My goal is to display the email textfield only when the user inputs the string "abc", however, the email field does not appear after entering "abc". I sus ...