Mat-table Sorting Demo not Working

AngularAngular Material

Angular Problem Overview


I am trying to get the mat-table sorting to work locally, and while I can get the data to show up as expected, clicking on the header row does not do the sorting as it does on online examples (nothing happens at all). I am trying to get this demo working locally: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

I have generated a new project with Angular CLI, then followed these steps: https://material.angular.io/guide/getting-started

Here are my local files:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatSort, MatTableModule } from '@angular/material';

import { AppComponent } from './app.component';
import { TableSortingExample } from './table-sorting-example';

@NgModule({
  declarations: [
    AppComponent,
    TableSortingExample,
    MatSort
  ],
  imports: [
    BrowserModule,
    MatTableModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
}

app.component.html

<div style="text-align:center">
  <h1>
    Welcome to {{title}}!
  </h1>
  <table-sorting-example></table-sorting-example>
</div>

table-sorting-example.html

<div class="example-container mat-elevation-z8">
  <mat-table #table [dataSource]="dataSource" matSort>

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- ID Column -->
    <ng-container matColumnDef="userId">
      <mat-header-cell *matHeaderCellDef mat-sort-header> ID </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container matColumnDef="progress">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Progress </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.progress}}% </mat-cell>
    </ng-container>

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

    <!-- Color Column -->
    <ng-container matColumnDef="color">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Color </mat-header-cell>
      <mat-cell *matCellDef="let row" [style.color]="row.color"> {{row.color}} </mat-cell>
    </ng-container>

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


<!-- Copyright 2017 Google Inc. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at http://angular.io/license -->

table-sorting-example.ts

import {Component, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk/collections';
import {MatSort} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';

/**
 * @title Table with sorting
 */
@Component({
  selector: 'table-sorting-example',
  styleUrls: ['table-sorting-example.css'],
  templateUrl: 'table-sorting-example.html',
})
export class TableSortingExample {
  displayedColumns = ['userId', 'userName', 'progress', 'color'];
  exampleDatabase = new ExampleDatabase();
  dataSource: ExampleDataSource | null;

  @ViewChild(MatSort) sort: MatSort;

  ngOnInit() {
    this.dataSource = new ExampleDataSource(this.exampleDatabase, this.sort);
  }
}

/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
  'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
  'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
  'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];

export interface UserData {
  id: string;
  name: string;
  progress: string;
  color: string;
}

/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
  get data(): UserData[] { return this.dataChange.value; }

  constructor() {
    // Fill up the database with 100 users.
    for (let i = 0; i < 100; i++) { this.addUser(); }
  }

  /** Adds a new user to the database. */
  addUser() {
    const copiedData = this.data.slice();
    copiedData.push(this.createNewUser());
    this.dataChange.next(copiedData);
  }

  /** Builds and returns a new User. */
  private createNewUser() {
    const name =
      NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
      NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';

    return {
      id: (this.data.length + 1).toString(),
      name: name,
      progress: Math.round(Math.random() * 100).toString(),
      color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
    };
  }
}

/**
 * Data source to provide what data should be rendered in the table. Note that the data source
 * can retrieve its data in any way. In this case, the data source is provided a reference
 * to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
 * the underlying data. Instead, it only needs to take the data and send the table exactly what
 * should be rendered.
 */
export class ExampleDataSource extends DataSource<any> {
  constructor(private _exampleDatabase: ExampleDatabase, private _sort: MatSort) {
    super();
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<UserData[]> {
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._sort.sortChange,
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      return this.getSortedData();
    });
  }

  disconnect() {}

  /** Returns a sorted copy of the database data. */
  getSortedData(): UserData[] {
    const data = this._exampleDatabase.data.slice();
    if (!this._sort.active || this._sort.direction == '') { return data; }

    return data.sort((a, b) => {
      let propertyA: number|string = '';
      let propertyB: number|string = '';

      switch (this._sort.active) {
        case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
        case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
        case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
        case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
      }

      let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
      let valueB = isNaN(+propertyB) ? propertyB : +propertyB;

      return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
    });
  }
}


/**  Copyright 2017 Google Inc. All Rights Reserved.
 Use of this source code is governed by an MIT-style license that
 can be found in the LICENSE file at http://angular.io/license */

Does anyone have an idea of why it would show up like the online table but lack the sorting functionality?

Angular Solutions


Solution 1 - Angular

For anyone else who may have this problem: The problem was I didn't read the API reference properly on the angular materials website, the part that said I had to import MatSortModule. After I changed my imports list in app.module.ts to

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

it worked fine

Solution 2 - Angular

I had a problem that the sorting function was working but it wasn't sorting properly. I realized that matColumnDef has to have the same name of the property of my class / interface that I am referencing in matCellDef.

According to the Angular Material documentation:

> By default, the MatTableDataSource sorts with the assumption that the sorted column's name matches the data property name that the column displays.

For exemple:

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

The name in the matColumnDef directive has to be the same as the name used in the <mat-cell> component.

Solution 3 - Angular

If the table is inside *ngIf, it won't be working. It will work if it is changed to [hidden]

Solution 4 - Angular

matColumnDef name and *matCellDef actual value name should be same

Example:

<ng-container matColumnDef="oppNo">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Opportunity Number</th>
    <td mat-cell *matCellDef="let element">{{element.oppNo}}</td>
</ng-container>

In my case oppNo is same for matColumnDef name and *matCellDef name and sorting working fine.

Solution 5 - Angular

I also hit this issue. Since you need to wait for the child to be defined, you have to implement and use AfterViewInit, not onInit.

  ngAfterViewInit (){
    this.dataSource.sort = this.sort;
  }

Solution 6 - Angular

One of the reasons MatSort might not work, is when it is added to a dataSource (i.e. this.dataSource.sort = this.sort) before it is defined. There can be multiple reasons for this:

  1. if you add the sort in ngOnInit. At this point the template is not yet rendered, so the MatSort you get with @ViewChild(MatSort, { static: true }) sort: MatSort; is undefined and understandably will not do anything. A solution for this issue is to move this.dataSource.sort = sort to ngAfterViewInit. When ngAfterViewInit is called your component is rendered, and MatSort should be defined.

  2. when you use *ngIf is your template on your table element or one if it's parent elements and this *ngIf causes your table not to be rendered at the moment you try to set the MatSort. For example, if you have *ngIf="dataSource.data.length > 0" on your table element (to only render it if there is data present) and you set this.dataSource.sort = this.sort right after you set this.dataSource.data with your data. The component view will not be rerendered yet, so MatSort will still be undefined.

In order to get MatSort to work and still conditionally show your table you could decide to replace the *ngIf with [hidden] as stated in multiple other answers. However, if you want to keep your *ngIf statement you can use the following solution. This solution works for Angular 9, I haven't tested it on previous versions so I'm not sure if it works there.

I found this solution here: https://github.com/angular/components/issues/10205

Instead of putting:

@ViewChild(MatSort) sort: MatSort;

use a setter for matSort. This setter will fire once matSort in your view changes (i.e. is defined the first time), it will not fire when you change your sorting by clicking on the arrows. This will look like this:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    this.dataSource.sort = sort;
}

If you have other functions that (programmatically) change the sorting, I'm not sure if it will fire again, I haven't tested this. If you wan't to make sure it only sets the sort if the sort was undefined, you can do something like this:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    if (!this.dataSource.sort) {
        this.dataSource.sort = sort;
    }
}

Solution 7 - Angular

Adding sort within timeout block works for me,

dataSource = new MatTableDataSource(this.articleService.getAllArticles());
setTimeout(() => {
  this.tableDataSource.sort = this.sort;
  this.tableDataSource.paginator = this.paginator;
});

If you don't want to use lifecykle hooks.

Solution 8 - Angular

I spent hours on this issue. After reading through a number of threads, here are the steps I did.

  1. As @avern mentioned, you need to import MatSortModule.
  2. Make sure you are NOT enclosing the table in a *ngIf. Change it to [hidden] as @zerg recommended. (I don't understand why)

Hope this helps.

Solution 9 - Angular

My solution was to fix several things (basically merging most of the solutions in this page).

Things to check:

  1. BrowserModule, MatTableModule, MatSortModule Modules should be imported in the root modules file.
  2. Make sure to have used MatTableDatasource class and pass your data array in it as a parameter
  3. Make sure your table is not nested in an *ngIf=.... directive. Use other conditional operations instead (still don't understand why).

Solution 10 - Angular

in your app.module.ts, do the following:

import

import { MatSortModule } from '@angular/material/sort';

then add

imports: [
    ...
    MatSortModule
],

Solution 11 - Angular

I fixed this in my scenario by naming the table data with same name as *matColumnDef For example:

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

Instead

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

Solution 12 - Angular

There were 2 issues for me.

  1. The matColumnDef and matCellDef -> names are different

  2. I was getting the data from the service. The ngOnInit sort was not working. Replaced with

    ngAfterViewInit() { this.dataSource.sort = this.sort; }

Solution 13 - Angular

I found this old blog which helped me get it to work: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Make sure to import MatSortModule
  2. Specify the matSort header
  3. Make sure to wrap your datasource in a MatTableDataSource
    • This is the one that helped me sort it out (get it? sort it out). In the template I was referring to the array directly (<table mat-table [dataSource]="this.products" matSort>) but I should have used the datasource object I initialized in the code (<table mat-table [dataSource]="this.dataSource" matSort>). The datasource is initialized like dataSource = new MatTableDataSource(this.products)
  4. Tell the data source about your sort, in ngOnInit/ngAfterViewInit
  5. Write your own sort, if you do not want to use MatTableDataSource

Solution 14 - Angular

For anyone that is confused about these namings having to be equal, I did some testing:

This will work (the name of the property is the same as the column def):

<ng-container matColumnDef="version">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version']

This will NOT work (the name of the property is not the same as the column def):

<ng-container matColumnDef="version2">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version2']

Fyi, this also does NOT work (the length of a property):

<ng-container matColumnDef="length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['length']

And neither does this:

<ng-container matColumnDef="ids.length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['ids.length']

Solution 15 - Angular

For me replacing *ngIf with [hidden] attribute for mat-table tag worked. How to post this one as a bug to Angular Material community?

Solution 16 - Angular

I found multiple answers to this question, but implementing them individually did not give me any results. So I tried to merge answers and it worked.

Firstly, I added ViewChild sort inside NgAfterViewInit interface. (It was initially inside a function that was called through NgOnInit

 ngAfterViewInit(){
    this.tableData.sort = this.sort;
  }

For the second step i changed the *ngIf inside a container to [hidden]. I do get an error that says that the value is not loaded. But it is not a major one to be concerned about till now.

before

<div class="mat-elevation-z0 container-fluid" *ngIf={some boolean resultant condition}>

after

<div class="mat-elevation-z0 container-fluid" [hidden] = {negation of your boolean expression}>

psst.. You can also consider adding a loading spinner while your table is being loaded through mat-footer to above the above bs.

    <ng-container matColumnDef="loading">
                    <mat-footer-cell *matFooterCellDef colspan=6>
                        <div class="uploader-status">
                            <mat-spinner strokeWidth="25" [diameter]="100" title="Server Starting" ></mat-spinner>
                        </div>
                    </mat-footer-cell>
</ng-container>


<mat-footer-row *matFooterRowDef="['loading']" [ngStyle]="{'display': (this.candidateService.candidateRecords!=null) ? 'none':'block'}"></mat-footer-row>

Solution 17 - Angular

If your table is inside an *ngIf and you think it has something to do with it not sorting your table, then specifying your own sortingDataAccessor function might solve the issue as it did for me. I have my table inside couple of *ngIfs and taking it out of those *ngIfs did not make sense:

`ngAfterViewInit(): void {
		this.matchesDataSource.sort = this.sort;
		this.matchesDataSource.sortingDataAccessor = previewMatchSortingFn;
	}`

`export function previewMatchSortingFn(item: Match, header: string): string | number {
	switch (header) {
		case 'home':
			return item.homeTeam.name;
		case 'away':
			return item.awayTeam.name;
		case 'date':
			if (item.dateTime) {
				// this will return the number representation of the date
				return item.dateTime.valueOf();
			}
			return;
		default:
			break;
	}
}`

Solution 18 - Angular

The below code was worked for me perfectly,

@ViewChild(MatSort) set matSort(sort: MatSort) {
if (!this.dataSource.sort) {this.dataSource.sort = sort;}}

Solution 19 - Angular

The main reasons why mat-sort & mat-paginator doesn't work are

  1. Modules MatSortModule & MatPaginatorModule are not imported
  2. Table is under *ngIf condition
  3. matColumnDef should be the same as matCellDef and displayedColumns array.

Solution 20 - Angular

After spending time on this for weeks. I found out you the following

  1. You need to import MatSortModule in your app.module.ts.

> imports: [ > ... > MatSortModule > ],

> > Ledger Transaction Credit > {{element.ledgerTransactionCreditSum}} > > matColumnDef and element.ledgerTransactionCreditSum variable and matcolumn def shoulde be same

  1. Define the sort and paginator in ngViewInit

>

ngAfterViewInit(): void {
    this.dataSource.sort = this.sort;
    this.dataSource.paginator = this.paginator;
    this.dataSource.paginator?._changePageSize(400)
  }

Solution 21 - Angular

If you read all answers until here and nothing helped, maybe you have the same problem I had.

The problem was that my MatTableDataSource object

dataSource = new MatTableDataSource<StbElement>(ELEMENT_DATA);

Was used in the html file without this.

Changing:

<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">

To:

<table mat-table [dataSource]="this.dataSource" matSort class="mat-elevation-z8">

Fixed the problem.

Solution 22 - Angular

In addition to all the previous answers, sometimes the table isn't visible at the time of data retrieval. For example, I had to display a table with MatSort and MatPaginator inside of a modal/dialog. Therefore, I had to pass in the elements through their respective Output emitter functions like so:

<... matSort #sort="matSort" (matSortChange)="sortData(sort)">
<... #paginator (page)="changePaginator(paginator)">

And in the typescript:

  @ViewChild(MatSort, { static: false }) set sort(s: MatSort) {
      this.dataSource.sort = s;
  }
  @ViewChild(MatPaginator, { static: false }) set paginator(p: MatPaginator) {
      this.dataSource.paginator = p;
  }

  sortData(sort: MatSort) {
      this.sort = sort;
  }
  changePaginator(paginator: MatPaginator) {
      this.paginator = paginator;
  }

Make sure to set the paginator's default values in its input directives since the above code will set the elements after paginating, i.e: [pageSize]="5" [length]="dataSource?.data?.length". Please use this as a last resort to all previous solutions.

Solution 23 - Angular

Actually matColumnDef name(i.e column name) and your Class/Interface property name should be equal to make it work.

Sometimes we can't change our Class/Interface property name, in that case, we can implement custom sorting as below.

let say your columns  as  ['id', 'name'] and 
your class/interface  as  ['userId', 'name']

if we perform sorting on the 'id' column it won't work. Try with the custom sorting

this.dataSource.sortingDataAccessor = (item,property)=>{

 // where item is your class/interface data
 // where property is your column name

       switch(property){
           case 'id' : return item.userId
           default: return item[property];
        }
}



Solution 24 - Angular

My solution for this problem is as below - 


1. These two lines will go in the same order.

	this.dataSource = new MatTableDataSource(myRowDataArray);// this dataSource is used in table tag.
	this.dataSource.sort = this.sort;


2. Pass MatTableDataSource object in [dataSource] 
	<table mat-table [dataSource]="dataSource">
	// rest of the table definition here
	</table>

3. By default, the MatTableDataSource sorts with the assumption that the sorted column's name matches the data property name that the column displays.

Example - 
	<ng-container matColumnDef="date" >
		  <th class="headers" mat-header-cell  *matHeaderCellDef mat-sort-header>Date</th>
		  <td class="data" mat-cell *matCellDef="let row">{{row.date|date}}</td>
	</ng-container>

4. If the table is inside *ngIf,then replace it with [hidden] or some other filter.

I missed the 2nd point.

Cheers!

Solution 25 - Angular

This issue mainly happens when sort is initialized before the dataSource. In the demo found here the dataSource is initialized statically as a result no issue happens. When you have to fetch data asynchronously, though, you need to wait for the response from API call to reach and get assigned to the dataSource before you initilize the sort instance variable.

Solution 26 - Angular

I don't know the reason; but putting this.dataSource.sort = this.sort; assignment to ngAfterViewInit() method didn't work. Even I confirmed this function is getting hitting after page loaded still was not working. The solution for me was putting sort assignment in ngOnInit() method.

 ngOnInit(): void {
   this.service.getAllAudits().subscribe(result => { 
    this.dataSource  = new MatTableDataSource(result); 
    this.dataSource.sort = this.sort; 
  });

}

Solution 27 - Angular

changing

@ViewChild('matsort') sort: MatSort;

to

@ViewChild(matSort) sort: MatSort

did it for me must be the same

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort matSortActive="name" matSortDirection="asc">

Solution 28 - Angular

Its working for me..

@ViewChild('sortConfigTable', { static: false }) sortConfigTable: MatSort;

after data initial assign

      this.dataSource.data = ds;

      setTimeout(() => {
        if (this.sortConfigTable) {
          this.dataSource.sort = this.sortConfigTable;
        }
      }, 1000);

Solution 29 - Angular

See if you have any javascript errors in the console. It could be that some other thing failed before your sorting initialized.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionavernView Question on Stackoverflow
Solution 1 - AngularavernView Answer on Stackoverflow
Solution 2 - AngularAndre EvangelistaView Answer on Stackoverflow
Solution 3 - AngularaungyeView Answer on Stackoverflow
Solution 4 - AngularSidduView Answer on Stackoverflow
Solution 5 - AngularflashapeView Answer on Stackoverflow
Solution 6 - AngularEmmyView Answer on Stackoverflow
Solution 7 - AngularChandrahasanView Answer on Stackoverflow
Solution 8 - AngularberrytchaksView Answer on Stackoverflow
Solution 9 - AngularSamuel MutemiView Answer on Stackoverflow
Solution 10 - AngularRusell Jane QuitainView Answer on Stackoverflow
Solution 11 - AngularspeksyView Answer on Stackoverflow
Solution 12 - AngularRaj kannan IyyappanView Answer on Stackoverflow
Solution 13 - AngularkumaheiyamaView Answer on Stackoverflow
Solution 14 - AngularWtFudgEView Answer on Stackoverflow
Solution 15 - AngularGaurav KohirkarView Answer on Stackoverflow
Solution 16 - AngularjoelView Answer on Stackoverflow
Solution 17 - AngularO.MeeKohView Answer on Stackoverflow
Solution 18 - AngularMahadevan AnnamalaiView Answer on Stackoverflow
Solution 19 - AngularIMRAN ABBASView Answer on Stackoverflow
Solution 20 - AngularShantam MittalView Answer on Stackoverflow
Solution 21 - AngularRafaelJanView Answer on Stackoverflow
Solution 22 - AngularAndrew SamoleView Answer on Stackoverflow
Solution 23 - Angularkasee nadh reddy bojjaView Answer on Stackoverflow
Solution 24 - AngularSHUBHAM RUHELAView Answer on Stackoverflow
Solution 25 - AngularDejazmachView Answer on Stackoverflow
Solution 26 - AngularnzrytmnView Answer on Stackoverflow
Solution 27 - AngularYzakView Answer on Stackoverflow
Solution 28 - Angularsweetnandha cseView Answer on Stackoverflow
Solution 29 - AngularΕ Г И І И ОView Answer on Stackoverflow