Search Sort Pagination in Angular 4

August 1, 2017
by Rahil Shaikh
  • Search Sort Pagination in Angular 2

Search Sort Pagination, The three most common features in any list. Let’s see how we can implement it in our Angular application.

The goal of this article is to teach to add search, sort, and pagination to a list displayed using the ngFor directive, also keeping the complexity to a minimum.

Start a new App

We will use angular CLI to speed up our development.

ng new game-list

Throughout the article, the word Angular refers to Angular 2 and above unless mentioned specifically otherwise. This tutorial uses Angular 4.

This will create a starter app for us to work with.

Navigate inside the directory using your CLI tool. And install all the dependencies (sometimes the dependencies fail to install, so we need to do it manually).

npm install

Overview and setup

In this article, we will cover..

  • Search filter in Angular
  • Sorting in an Angular list
  • Pagination in Angular

Before we begin with any of the three, we must lay out the foundation work for our application. Below, I will be adding the bootstrap library to add a bit of style to our app and will also display a list using ngFor on which we can perform our above operations.

npm install bootstrap --save

To load the CSS file we need to add it into .angular-cli.json file.

.angular-cli.json
....,
..,
      "styles": [
        "styles.css",
        "../node_modules/bootstrap/dist/css/bootstrap.min.css"
      ],
...,
...

Let’s create a list to display in the root component.

/src/app/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 works!';
  games = [
    {
      "id":"1",
      "name": "DOTA 2",
      "genre": "Strategy"
    },
    {
      "id":"2",
      "name": "AOE 3",
      "genre": "Strategy"
    },
    {
      "id":"3",
      "name": "GTA 5",
      "genre": "RPG"
    },
    {
      "id":"4",
      "name": "Far Cry 3",
      "genre": "Action"
    },
    {
      "id":"5",
      "name": "GTA San Andreas",
      "genre": "RPG"
    },
    {
      "id":"6",
      "name": "Hitman",
      "genre": "Action"
    },
    {
      "id":"7",
      "name": "NFS MW",
      "genre": "Sport"
    },{
      "id":"8",
      "name": "Fifa 16",
      "genre": "Sport"
    },{
      "id":"9",
      "name": "NFS Sen 2",
      "genre": "Sport"
    },{
      "id":"10",
      "name": "Witcher Assasins on King",
      "genre": "Adventure"
    }
  ]
}

Now we will display this list using ngFor directive.

/src/app/app.component.html
<header>
  <nav class="navbar navbar-default">
    <a class="navbar-brand" href="#">Ma Games</a>
  </nav>
</header>

<div class="container">
  <div class="row">
    <nav class="navbar">
      <input class="form-control" type="text" name="search">      
    </nav>
    <table class="table">
      <thead>
        <tr>
          <th>#</th>
          <th>Name</th>
          <th>Genre</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let game of games; let i = index">
          <td>{{i}}</td>
          <td>{{game.name}}</td>
          <td>{{game.genre}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Run the app ng serve. You should see this.

//image

Search filter in Angular

If you have worked with Angular v1, you know how easy it was to add a searching facility in ng-repeat using the filter filter. In Angular 2+ we have pipes which are equivalent to filters of Angular 1. But, unfortunately, the Angular team has not included a pre-built search pipe. So either we need to use a third-party library or we can build our own.

Here we will use the ng2-search-filter library to achieve this. Let’s start by installing it.

npm i ng2-search-filter --save

Since we have used Angular CLI for development, we do not have to worry about configuring it in webpack, systemjs or any other module loader. Angular CLI takes of this for us internally.

Now we need to import this into our app.module.ts.

/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { Ng2SearchPipeModule } from 'ng2-search-filter'; //importing the module

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    Ng2SearchPipeModule //including into imports
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

In our root component’s template, we will add an [(ngModel)] to our search bar and add the filter pipe provided by our included library to ngFor. Only two lines will be modifed in the template as shown below.

/src/app/app.component.html
...
..
..
    <nav class="navbar">
      <input class="form-control" type="text" name="search" [(ngModel)]="filter">      
    </nav>
....
...
...
        <tr *ngFor="let game of games | filter:filter; let i = index">
          <td>{{i}}</td>
          <td>{{game.name}}</td>
          <td>{{game.genre}}</td>
        </tr>
...
..
..

Run the app and hurray our search should be working.

Implement Sort feature in Angular list

Next we will add sorting to our list.

Again, there isn’t a pre-built pipe to carry out sorting in Angular. So we will rely on ng2-order-pipe npm package to implement it.

Let’s start by installing the package.

npm install ng2-order-pipe --save

Once it’s installed we need to import it into our app module, just like we did for the search package above.

I won’t repeat the entire app.module.ts below, rather only the parts modified.

/src/app/app.module.ts
....
..
//other imports
...
import { Ng2OrderModule } from 'ng2-order-pipe'; //importing the module

@NgModule({
..
..
 ...
  imports: [
    ...,
   ......,
    Ng2OrderModule //add here
  ],
  ..
...
})
export class AppModule { }

The usage of orderBy the pipe is very similar to the orderBy filter provided in Angular v1. Let’s examine it.

{{ collection | orderBy: key : reverse }}

Here,
collection is an array or an object on which sorting will be performed.
orderBy is the pipe provided by our library.
key is the key on which sorting will be performed.
reverse is a boolean which maintains the order of the sorting (asc or desc)

Now, that we know how it works, let’s add it to our list.

/src/app/app.component.html
...
...
...
        <tr *ngFor="let game of games | orderBy: key : reverse | filter:filter; let i = index">
          <td>{{i}}</td>
          <td>{{game.name}}</td>
          <td>{{game.genre}}</td>
        </tr>
...
..
..

This won’t work yet. Because we haven’t yet defined the key and reverse in our class. We will also add a sort(key) method which will be called on the click of the table headers.

/src/app/app.component.ts
....
...
..
export class AppComponent {
..
..
..
  //sorting
  key: string = 'name'; //set default
  reverse: boolean = false;
  sort(key){
    this.key = key;
    this.reverse = !this.reverse;
  }
}

Now, in our template, we need to call the sort method with appt values on click of table headers.

/src/app/app.component.html
...
...
...
       <thead>
        <tr>
          <th>#</th>
          <th (click)="sort('name')">Name</th>
          <th (click)="sort('genre')">Genre</th>
        </tr>
      </thead
...
..
..

This should be enough to get our sorting working, but we need to improve a bit of UX. We need to add some indicators that will inform the user of the column and order of sorting.

Since we are using Bootstrap, we can use glyphicon.

<span class="glyphicon sort-icon" *ngIf="key =='name'" [ngClass]="{'glyphicon-chevron-up':reverse,'glyphicon-chevron-down':!reverse}"></span>

Adding this to both the columns, our table header should look like below.

/src/app/app.component.html
...
...
...
       <thead>
        <tr>
          <th>#</th>
          <th (click)="sort('name')">Name
            <span class="glyphicon sort-icon" *ngIf="key =='name'" [ngClass]="{'glyphicon-chevron-up':reverse,'glyphicon-chevron-down':!reverse}"></span>
          </th>
          <th (click)="sort('genre')">Genre
            <span class="glyphicon sort-icon" *ngIf="key =='genre'" [ngClass]="{'glyphicon-chevron-up':reverse,'glyphicon-chevron-down':!reverse}"></span>
          </th>
        </tr>
      </thead>
...
..
..

I have also gone ahead and added a bit of CSS to the component CSS file.

/src/app/app.component.css
thead>tr>th {
    cursor: pointer;
}

Save it, and run the application. The sorting should work now.

We have search and sort both implemented, let’s work towards adding pagination.

Implement Pagination in Angular

Pagination has always been a complicated subject for UI developers. Here I will try to make pagination implementation as simple as possible.

We will use the ngx-pagination module written by Michael Bromley.

Let’s install the npm package.

npm install ngx-pagination --save

As we did with our other modules, we need to import ngx-pagination in our app module.

/src/app/app.module.ts
....
..
//other imports
...
import {NgxPaginationModule} from 'ngx-pagination'; // <-- import the module

@NgModule({
..
..
 ...
  imports: [
    ...,
   ......,
    NgxPaginationModule//add here
  ],
  ..
...
})
export class AppModule { }

Ngx-Pagination provides us with a paginate pipe that is responsible for paginating the data and a pagination-controls component which will display the navigation links.

Next, add the paginate pipe and the pagination-controls component to our root component template as shown below.

/src/app/app.component.html
...
...
...
<tbody>
        <tr *ngFor="let game of games | orderBy: key : reverse | filter:filter | paginate: { itemsPerPage: 5, currentPage: p }; let i = index">
          <td>{{i}}</td>
          <td>{{game.name}}</td>
          <td>{{game.genre}}</td>
        </tr>
      </tbody>
      <pagination-controls (pageChange)="p = $event"></pagination-controls>
...
..
..

And in our class initialize the currrentPage which is p to 1.

/src/app/app.component.ts
....
...
..
export class AppComponent {
..
..
..
  //sorting
  key: string = 'name'; //set default
  reverse: boolean = false;
  sort(key){
    this.key = key;
    this.reverse = !this.reverse;
  }

  //initializing p to one
  p: number = 1;
}

That is it. Save and run the application, pagination should be working now along with search and sort.

ngx-pagination comes with a lot of options, which I haven’t covered here. For eg: It supports multiple instances on the same page using the pagination has support for server side paging using the totalItems option and so on. You should definitely check out the GitHub page for more info.

Final Files

In case you have missed out on anything, here are the final files.

package.json
{
  "name": "search-sort-pagination-angular2",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "^4.0.0",
    "@angular/compiler": "^4.0.0",
    "@angular/core": "^4.0.0",
    "@angular/forms": "^4.0.0",
    "@angular/http": "^4.0.0",
    "@angular/platform-browser": "^4.0.0",
    "@angular/platform-browser-dynamic": "^4.0.0",
    "@angular/router": "^4.0.0",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "ng2-order-pipe": "^0.1.5",
    "ng2-search-filter": "^0.3.1",
    "ngx-pagination": "^3.0.1",
    "rxjs": "^5.1.0",
    "zone.js": "^0.8.4"
  },
  "devDependencies": {
    "@angular/cli": "1.0.3",
    "@angular/compiler-cli": "^4.0.0",
    "@types/jasmine": "2.5.38",
    "@types/node": "~6.0.60",
    "codelyzer": "~2.0.0",
    "jasmine-core": "~2.5.2",
    "jasmine-spec-reporter": "~3.2.0",
    "karma": "~1.4.1",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "karma-coverage-istanbul-reporter": "^0.2.0",
    "protractor": "~5.1.0",
    "ts-node": "~2.0.0",
    "tslint": "~4.5.0",
    "typescript": "~2.2.0"
  }
}

 

/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { Ng2SearchPipeModule } from 'ng2-search-filter'; //importing the module
import { Ng2OrderModule } from 'ng2-order-pipe'; //importing the module
import {NgxPaginationModule} from 'ngx-pagination'; // <-- import the module

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    Ng2SearchPipeModule, //including into imports
    Ng2OrderModule, // importing the sorting package here
    NgxPaginationModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

/src/app/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 works!';
  games = [
    {
      "id":"1",
      "name": "DOTA 2",
      "genre": "Strategy"
    },
    {
      "id":"2",
      "name": "AOE 3",
      "genre": "Strategy"
    },
    {
      "id":"3",
      "name": "GTA 5",
      "genre": "RPG"
    },
    {
      "id":"4",
      "name": "Far Cry 3",
      "genre": "Action"
    },
    {
      "id":"5",
      "name": "GTA San Andreas",
      "genre": "RPG"
    },
    {
      "id":"6",
      "name": "Hitman",
      "genre": "Action"
    },
    {
      "id":"7",
      "name": "NFS MW",
      "genre": "Sport"
    },{
      "id":"8",
      "name": "Fifa 16",
      "genre": "Sport"
    },{
      "id":"9",
      "name": "NFS Sen 2",
      "genre": "Sport"
    },{
      "id":"10",
      "name": "Witcher Assassins on King",
      "genre": "Adventure"
    }
  ];

  //sorting
  key: string = 'name';
  reverse: boolean = false;
  sort(key){
    this.key = key;
    this.reverse = !this.reverse;
  }
  p: number = 1;
}

 

/src/app/app.component.html
<header>
  <nav class="navbar navbar-default">
    <a class="navbar-brand" href="#">Ma Games</a>
  </nav>
</header>

<div class="container">
  <div class="row">
    <nav class="navbar">
      <input class="form-control" type="text" name="search" [(ngModel)]="filter">      
    </nav>
    <table class="table">
      <thead>
        <tr>
          <th>#</th>
          <th (click)="sort('name')">Name
            <span class="glyphicon sort-icon" *ngIf="key =='name'" [ngClass]="{'glyphicon-chevron-up':reverse,'glyphicon-chevron-down':!reverse}"></span>
          </th>
          <th (click)="sort('genre')">Genre
            <span class="glyphicon sort-icon" *ngIf="key =='genre'" [ngClass]="{'glyphicon-chevron-up':reverse,'glyphicon-chevron-down':!reverse}"></span>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let game of games | orderBy: key : reverse | filter:filter | paginate: { itemsPerPage: 5, currentPage: p }; let i = index">
          <td>{{i}}</td>
          <td>{{game.name}}</td>
          <td>{{game.genre}}</td>
        </tr>
      </tbody>
      <pagination-controls (pageChange)="p = $event"></pagination-controls>
    </table>
  </div>
</div>

Code

You can also browse the entire code on this GitHub Repo.

Conclusion

In this article we have learned arguably the easiest way to implement search sort pagination on a list displayed using ngFor in Angular 2. Hope this tutorial helps you pace up your project development.

 

About

Engineer. Blogger. Thinker. Loves programming and working with emerging tech. We can also talk on Football, Gaming, World Politics, Monetary Systems.

Free PDF

Subscribe and get AngularJS Tips PDF. We never spam!
First Name:
Email:

8 comments

  1. |

    Hi Rahil,

    I appreciate and use the code you publish. I have learned a lot, just using and modifying your code

    • |

      Thanks Don,
      It’s always great to hear from you. 🙂

  2. |

    Thanks a lot for this great explanation and code.

  3. |

    How can i display the pagination on list of rows which are selected from dropdown .
    Lets say if there are 100 records, if i select 15 from dropdown list, it should display in 7 pages,if i select 50 then it should display in 2 pages and if i select 100 it should display only one 1 page.

    • |

      You only need to set the value of itemsPerPage from the component class depending on the value selected in the dropdown.

  4. |

    can we sort the date ,i want the logic to sort the date.

  5. |

    hai rahil amzing tutorial dude, very useful for my team please post more angular 4 ionic 3 blogs we are waiting dude

Leave a Comment