Angular 2 Search and Sort with ngFor repeater with example

angular 2 sort table angular 2 search pipe angular 2 orderby pipe angular 2 orderby alphabetical angular 2 sort array of objects angular 2 orderby pipe example the pipe 'orderby' could not be found typescript sort array

In angular 2 we don't have pre-defined filter and order by as it was with AngularJs, we need to create it for our requirements. It is time killing but we need to do it, (see No FilterPipe or OrderByPipe). In this article we are going to see how we can create filter called pipe in angular 2 and sorting feature called Order By. Let's use a simple dummy json data array for it. Here is the json we will use for our example

First we will see how to use the pipe (filter) by using the search feature:

Create a component with name category.component.ts

import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'app-category',
  templateUrl: './category.component.html'
})
export class CategoryComponent implements OnInit {

  records: Array<any>;
  isDesc: boolean = false;
  column: string = 'CategoryName';
  constructor() { }

  ngOnInit() {
    this.records= [
      { CategoryID: 1,  CategoryName: "Beverages", Description: "Coffees, teas" },
      { CategoryID: 2,  CategoryName: "Condiments", Description: "Sweet and savory sauces" },
      { CategoryID: 3,  CategoryName: "Confections", Description: "Desserts and candies" },
      { CategoryID: 4,  CategoryName: "Cheeses",  Description: "Smetana, Quark and Cheddar Cheese" },
      { CategoryID: 5,  CategoryName: "Grains/Cereals", Description: "Breads, crackers, pasta, and cereal" },
      { CategoryID: 6,  CategoryName: "Beverages", Description: "Beers, and ales" },
      { CategoryID: 7,  CategoryName: "Condiments", Description: "Selishes, spreads, and seasonings" },
      { CategoryID: 8,  CategoryName: "Confections", Description: "Sweet breads" },
      { CategoryID: 9,  CategoryName: "Cheeses",  Description: "Cheese Burger" },
      { CategoryID: 10, CategoryName: "Grains/Cereals", Description: "Breads, crackers, pasta, and cereal" }
     ];
     // this.sort(this.column);
  }
}

Nothing special in this code just initialize our records variable with a list of categories, two other variables isDesc and column are declared which we will use for sorting latter. At the end added this.sort(this.column); latter we will use, once we will have this method.

Note templateUrl: './category.component.html', which we will create next to show the records in tabluar format.

For this create a HTML page called category.component.html, whith following code:

<div class="col-md-12">
  <table class="table table-responsive table-hover">
    <tr>
      <th >Category ID</th>
      <th>Category</th>
      <th>Description</th>
    </tr>
    <tr *ngFor="let item of records">
      <td>{{item.CategoryID}}</td>
      <td>{{item.CategoryName}}</td>
      <td>{{item.Description}}</td>
    </tr>
  </table>
</div>

Here we use ngFor to repeat the records and show row by row, try to run it and we can see all records in a table.

Search - Filter Records

Say we want to search the table by category name, for this let's add one text box to type and search

<div class="form-group">
  <div class="col-md-6" >
    <input type="text" [(ngModel)]="searchText" 
           class="form-control" placeholder="Search By Category" />
  </div>
</div>

Now we need to create a pipe to search the result by category because filter is not available as it was in angularjs any more.

Create a file category.pipe.ts and add following code in it.

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'category' })
export class CategoryPipe implements PipeTransform {
  transform(categories: any, searchText: any): any {
    if(searchText == null) return categories;

    return categories.filter(function(category){
      return category.CategoryName.toLowerCase().indexOf(searchText.toLowerCase()) > -1;
    })
  }
}

Here in transform method we are accepting the list of categories and search text to search/filter record on the list. Import this file into our category.component.ts file, we want to use it here, as follows:

import { CategoryPipe } from './category.pipe';
@Component({      
  selector: 'app-category',
  templateUrl: './category.component.html',
  pipes: [CategoryPipe]   // This Line       
})

Our ngFor loop now need to have our Pipe to filter the records so change it to this

<tr *ngFor="let item of records | category: searchText">

Try it and it will filter the record with our entered text into search text box.

Sort - Order By: on table columns

We will use font-awesome icone to show the sort direction, for it we are going to use

  1. Sortable: fa fa-sort
  2. Ascending: fa fa-sort-asc
  3. Descending: fa fa-sort-desc

We need to use these classes conditionally, for it we need two value sorting property say column and sort direction say isDesc, which already declared in our controller. Default sorting by CategoryName and direction ascending.

Let's see how we can apply these three classes conditionally:

<i class="fa" 
    [ngClass]="{'fa-sort': column != 'CategoryID', 
                        'fa-sort-asc': (column == 'CategoryID' && !isDesc), 
                        'fa-sort-desc': (column == 'CategoryID' && isDesc) }"
    aria-hidden="true"> </i>

Add this line in every header (th) and change the conditoin with column name to show the direction, let me clear it, not very difficult:

  1. First check if sorted column is not the current column then show sortable icone
  2. If sorted column is current column and direction is not descending
  3. If sorted column is current column and direction is descending
  4. Use it conditionally with ngClass so only one will be applied
  5. We keept fa to class because that was common in every condition

Add click event to every header to change the sorted column and direction on every click and sort the records.

<th class="pointer" (click)="sort('CategoryID')"> // category Id
<th class="pointer" (click)="sort('CategoryName')" > // Category Name
<th class="pointer" (click)="sort('Description')" >  // Description 

We added here a class pointer to change the mouse cursor to show hand and call the sort function on click and pass the column name.

// CSS class to change the mouse cursor in hand
.pointer{
    cursor: pointer;
}

We need to define our sort function so let's to do it.

sort(property){
    this.isDesc = !this.isDesc; //change the direction    
    this.column = property;
    let direction = this.isDesc ? 1 : -1;

    this.records.sort(function(a, b){
        if(a[property] < b[property]){
            return -1 * direction;
        }
        else if( a[property] > b[property]){
            return 1 * direction;
        }
        else{
            return 0;
        }
    });
};
  • First of all we change the direction
  • Change the sort column name to currently clicked column
  • We use the sort method of array to sort our records.

When I was testing I found it is working well without filter but once I add the filter, it stop showing any result. To fix this let's make some changes and move our sorting funtion to a pipe.

Here is the changes in our above code:

// Declare local variable
direction: number;
// Change sort function to this: 
sort(property){
    this.isDesc = !this.isDesc; //change the direction    
    this.column = property;
    this.direction = this.isDesc ? 1 : -1;
}

Create a new orderBy pipe and move all the sorting featur to it, here is complete pipe code:

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({  name: 'orderBy' })
export class OrderrByPipe implements PipeTransform {

    transform(records: Array<any>, args?: any): any {
        return records.sort(function(a, b){
            if(a[args.property] < b[args.property]){
                return -1 * args.direction;
            }
            else if( a[args.property] > b[args.property]){
                return 1 * args.direction;
            }
            else{
                return 0;
            }
        });
    };
}

Here nothing is new just we moved the code to a pipe for better user.

We need to import both the pipes into our app.module.ts file, add them in declarations section

import { CategoryPipe } from './directives/category.pipe';
import { OrderrByPipe } from './directives/orderby.pipe';

@NgModule({
  declarations: [
    AppComponent,
    ............
    CategoryPipe,    // Note these two line
    OrderrByPipe,    // Note these two line
  ],
  imports: [
    .........
  ],
  providers: [.......],
  bootstrap: [AppComponent]
})

It's recomended to import and add the pipes into declarations section so it can be accessible from entire application, if we want to use only from a single page then we can use the pipes: [....] and it will be available for that component only.

Now time to give the final tough for our orderBy, change the ngFor like this:

<tr *ngFor="let item of  records | category: searchText 
                | orderBy: {property: column, direction: direction}">

Now we nicely completed it. Any suggestion or question is most welcome.

Ali Adravi Having 10+ years of experience in Microsoft Technologies (C#, ASP.Net, MVC and SQL Server). Worked with Metaoption LLC, for more than 8 years and still with the same company. Always ready to learn new technologies and tricks.
  • angular-2
  • angularjs
  • pipe
  • filter
By Ali Adravi On 16 Apr, 17  Viewed: 706

Other blogs you may like

Angularjs CRUD with Web API, Entity Framework & Bootstrap modal popup part 2

Add/Edid Customer in Modal Popup --- In previous article we discussed how to search, delete and show detail of customer and contact list. In this article we will try to open the modal popup to add new customer and edit an existing customer record. I divided article in two parts because of the... By Ali Adravi   On 29 Jul 2015  Viewed: 12,292

Custom Directives in AngularJS with example and demo

Angularjs custom directives are the common to avoid the duplicate code, it is easy powerful and really nice. There are already too many directive have been created by experts but as a developer we need to write our own to achieve our specific goal. ng-model, ng-if, ng-show, ng-repeat all these are... By Ali Adravi   On 29 Jul 2015  Viewed: 2,263

Angularjs CRUD with Web API, Entity Framework & Bootstrap modal popup part 1

Search sort Insert update and delete are the basic features we need to learn first to learn any language, In this article we will try to create these features with Web API, bootstrap modal popup and ui.router. We will use enetity framework code first to save and retrieve data from database. We... By Ali Adravi   On 28 Jul 2015  Viewed: 22,188

Angularjs Progress bar directive for entire application

Progress bar for Angularjs application by using directives is simple and real easy. I was googling and could not found any directive way, if you will check most of the people says before call the http method open the progress bas and close once you get the result or get any error. Do you think it... By Ali Adravi   On 24 Jul 2015  Viewed: 10,088

Apply angularjs animation in 2 minutes

Angularjs animation is more than easy, even you don't have write a single line of code any kind of animation. In this demo I am going to show how you can animate the list of item coming from left, rotating and flying list items by just adding ngAnimation dependency and some copy and past of css and... By Ali Adravi   On 21 Jul 2015  Viewed: 1,934