Angularjs powerful paging on table and searching with delay and demo

angularjs paging grid angularjs paging server side angularjs pagination table angularjs table paging angularjs bootstrap table angularjs sortable table angularjs pagination table example angularjs table json

Paging on table is the basic requirement and most of us look for some grid so it can provide the complete feature of paging and sorting but in Angularjs it is so easy that anyone can achieve it by writing some code. I checked and found most of the available code give the paging feature in for 1 to N means if there is 500 pages then it renders 500 links at the bottom which grow more than the table itself. What we are going to achieve is, maximum 9 items with previous and next feature, if more than 5 pages then previous & next button and two more item before and after the current page and first and last page option, so it can never grow more than 9 items.

For it we need to write in our service to return the paginated data and total number of records in current criteria, let's keep the criteria on scope before explaining it further

$scope.criteria={
    searchtext:'',
    page: 1,
    pagesize: 20,
    sort: 'Name',
    desc : false 
};

We will bind all the controls with criteria on our page, every variable is self explanatory. Add one more variable paging which we will use to calculate the page array, it is also simple, just added some variable and assigned default values, most of them are zero. Page Array as an array to keep all the paging items in it which we will see latter in this article. PagingOption array is to show the option to change the page size in dropdown, for it we need an array so we created one.

$scope.paging ={
    total : 0,
    totalpages: 0,
    showing: 0,
    pagearray: [],
    pagingOptions: [5,10,20,30,40,50]
};

Powerful pagination image

Search is easy so let's see it first, when we add watch it immediately calls after any property changed in the watch, which is not good, it will increase the load on server to fetch data again and again after every character typed by the use, so we will try to delay for 1 second to stop user to typing and only then we will search the record

 <div class="input-group">
    <input type="text"
           ng-model ="criteria.searchtext""  
           ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
           class="form-control" 
           placeholder="Search" />
    <div class="input-group-btn">
      <button class="btn btn-default" type="button">
        <i class="glyphicon glyphicon-search"></i></button>
    </div>
</div>

There is nothing special in it except the delay which we are talking now, see ng-model-options which used debounce to delay, it is a new feature in Angularjs 1.3, directly we can assign the value to it (some time) but it's good practice to give the default value and on blur immediately change the model value, which I am doing in this example. Here is the code in our controller to watch the changes in model

$scope.$watch('criteria', function(newValue, oldValue){
    if(!angular.equals(newValue, oldValue)){
        $scope.search();
    }
}, true);

Note, we use the criteria as parameter and at the end true, so any change in any property in it, will automatically call this function. search is a function which is searching record from a JSON customer list, which we will see next.

$scope.search = function(){
    var response = CustomerService.SearchCustomer(parameters);
    $scope.customers =  response.Customers;
    $scope.paging.showing = response.Customers.length;
    $scope.paging.total = response.TotalRecords; 
    // Manage the paging        
    paging($scope.criteria.page, $scope.criteria.pagesize, $scope.paging.total);
}

If you can note, we are calling a service method SearchCustomer on service CustomerService, which is not defined anywhere so please look for it. You can create a method in your service which can search the record and return only paged data say 10 and also give the total number of records for that search say 150, it is needed for our next method paging which we are calling at the end of the search function.

Let's see the paging method part by part to understand what's going on in it.

  • Pass three parameters
    • current: current page
    • pagesize: page size of the search
    • total: total record which match the search criteria

Let's see the code, part by part:

Very first divide the total reocrds by page size to get the total number of pages, by using Math.cei so it can count the reminder as one, I mean to say if there 22 record and page size is 10 then total pages should be 3.

function paging(current, pagesize, total){
   var totalpages = Math.ceil(total/pagesize);
   // Assign to the scope in paging 
   $scope.paging.totalpages = totalpages;
   // clear array before pushing new items
   $scope.paging.pagearray = [];

   // if page is 1 or 0 return, no need of paging
   if(totalpages <=1) return;

So for so good, I don't want to show Prev, Next button if pages are less than 5 so, push total pages one by one in a for loop.

if(totalpages <= 5){
    for(var i =1; i<= totalpages; i++)
        $scope.paging.pagearray.push(i);
}

If pages are more than 5 and current page is less than or equal to 3, show 1, 2, 3 ... LP, Next

if(totalpages > 5){
  if(current <=3){        
    for(var i =1; i<= 5; i++)
      $scope.paging.pagearray.push(i);

    $scope.paging.pagearray.push('...');
    $scope.paging.pagearray.push(totalpages);
    $scope.paging.pagearray.push('Next');
  }

If user is near to last page, 3 less than or equal to last page then show something like this: Prev, 1 ... 45, 46, 47, 49, 50

else if(totalpages - current <=3){
    $scope.paging.pagearray.push('Prev');
    $scope.paging.pagearray.push(1);
    $scope.paging.pagearray.push('..');
    for(var i =totalpages - 4; i<= totalpages; i++)
      $scope.paging.pagearray.push(i);
}

If user is neither in begining nor near to end then show: Prev 1, ..., 40, 41, 42, 43, 44 ... 55 Next

    else {
        $scope.paging.pagearray.push('Prev');
        $scope.paging.pagearray.push(1);
        $scope.paging.pagearray.push('..');  

        for(var i = current - 2; i<= current + 2; i++)
          $scope.paging.pagearray.push(i);

        $scope.paging.pagearray.push('...');
        $scope.paging.pagearray.push(totalpages);
        $scope.paging.pagearray.push('Next');
        }        
    }      
} 

Just I divided the code by some text to explain, if you will remove the text from code for paging function, it will give the complete working code for paging function, I know it is not re-usable, if you want to make it re-usable, create a service and move all the code in that serive and use it where ever you want to use it in your entire application.

Still we need to add the HTML, let me confirm you one thing, if you want to use the dropdown version then there is no need of paging function just move the code for calculating the total pages in search and assign that to the scope there and remove entire paging function every thing will work smooth.

var totalpages = Math.ceil(total/pagesize); 
// Assign to the scope in paging 
$scope.paging.totalpages = totalpages;

So let's look the dropdown version of paging HTML first:

<!-- Dropdown Style -->  
<div class="row" style="background-color:#f1e1d1; padding:7px;">
    <div class="col-lg-6 ">                              
        Showing: {{(criteria.page -1) * criteria.pagesize + 1}} - 
                 {{ ((criteria.page -1) * criteria.pagesize + paging.showing)}} 
        of {{paging.total}} Records
    </div>   

    <div class="col-lg-6 ">  
     <div class="pull-right">                           
        Page: <input type='number' 
                     ng-model='criteria.page'
                     min="1"
                     max="{{paging.totalpages}}"
                     style='width:70px;' />
        of {{paging.totalpages}}
        with page size <select ng-model="criteria.pagesize"                
                      ng-options="obj as obj for obj in paging.pagingOptions" />  
       </div>                                                   
    </div>                
</div>  

If you will look closely, there is nothing except some very common and easy calculation and simple binding.

Now we are going to see the HTML part of number version of paging, we need to render the anchor tag conditionally

<div class="row">
  <div class="pull-right">                               
    <ul class="pagination" >                           
      <li ng-class="{'active': item === criteria.page}" ng-repeat='item in paging.pagearray'>

        <a ng-if="item == 'Prev'" ng-click="Prev()">Prev</a>

         <!-- Only for numbers -->
         <a ng-if="(!(item == 'Prev' || item == 'Next' ||  item == '..' || item == '...'))" 
            ng-click="criteria.page = item;" >{{item}}</a>

         <!-- No click event -->                                          
         <a ng-if="(item == '..' || item == '...')" >{{item}}</a> 

         <a ng-if="item == 'Next'" ng-click="Next()">Next</a>
      </li>
    </ul>
   </div>  
</div>  

Simple ng-if to show hide the anchor tag and add the click events for different buttons, for numbered paging we are not showing any function but directly assigning the value to the page variable in criterai, and same we will do in Prev and Next function as well, which will cause the $watch to call and execute the search method, here is the simplest Prev and Next function

$scope.Prev = function(){
    if($scope.criteria.page >= 1)      
      $scope.criteria.page--;
}

$scope.Next = function(){
    if($scope.criteria.page < $scope.paging.totalpages)      
      $scope.criteria.page++;
}

Here is the demo of the above discussed article, I will suggest not to use the search method code in your code, because I am reading everything here from a JSON list just copied on the bottom of the JavaScript file. In your real life project you will need to get the data from service from the database. If you have any question, please post that here, we will be more than happy to help you.

I don't think you need the table ng-repeat binding code but let me copy that as well if someone need it:

<table class="table table-border">
  <tr>
    <th ng-click="criteria.sort = 'Name'; ">
      Customer  Name       
    </th>
    <th ng-click="criteria.sort = 'City';">
      City
    </th>
    <th ng-click="criteria.sort = 'Country';">
      Country
    </th>
  </tr>
  <tr class="animate" ng-repeat="customer in customers">
    <td ng-bind="customer.Name"></td>
    <td ng-bind="customer.City"></td>
    <td ng-bind="customer.Country"></td>
  </tr>
</table>
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.
  • angularjs
  • table
  • pagination
  • searching
By Ali Adravi On 14 Aug, 15  Viewed: 7,408

Other blogs you may like

Angularjs Cascading Dropdown/Select with demo

Cascading dropdown with angularjs is easier than MVC and ASP.Net. In Angularjs ng-change is the best event to hook the function to get the second dropdown list items. In this article, we will try to fill the state dropdown on selection change of country and then fill the city dropdown on change... By Ali Adravi   On 06 Aug 2015  Viewed: 30,437

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,273

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,256

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,146

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,070