Wednesday, December 12, 2012

Show More pagination with angular.js

I built my first application with angular.js over these last few weeks (not during business hours), and although I still have lots and lots to discover and learn, I think I somewhat grasp the basics.

In the application I built, I had to implement paging because rendering all the items at once was too slow on mobile devices (on my Windows Phone 7 anyways). The paging variant I decided on was the 'Show More' technique.

Let me walk you - as an introduction to Angular - through a simple application that uses paging for a list of 24 items.

We start by defining a module. In an Angular module, you should instantiate, wire and bootstrap parts of your application.
var module = angular.module('module', []);
We can already bind the module to our empty view.
<div ng-app="module">
</div>
In our module we want to define an itemService which will return an array of 24 items. By using a factory method we make our service an injectable dependency for other parts of our application.
module.factory('itemService', function() {
    return {
        getAll : function() {
            var items = [];
            for (var i = 1; i < 25; i++) {
                items.push('Item ' + i);                       
            }
            return items;
        }
    };              
});
Now that we have a service that can give us items, we want to work towards actually rendering these items. To do that, we'll have to define a controller. An Angular controller lets you augment an instance of Angular's scope, which in its turn serves as your connection to the view. 
ListController = function($scope, itemService) {
    $scope.items = itemService.getAll();    
};
Notice how we use dependency injection to inject an instance of the itemService.

All our items are available on the scope now, but we still have an empty view. Let's fix that.
<div ng-app="module" ng-controller="ListController">
    <ul>
       <li ng-repeat="item in items">
          {{ item }}        
       </li>               
    </ul>
    <button>Show more</button>    
</div>
Items are being rendered making use of Angular's built-in ng-repeat directive. Directives are - as the documentation puts it nicely - a way to teach HTML new tricks. MVC folks may think of it as HTML helpers.

What's left to do in this example, is what we initially set out to do; implement paging. For that, we'll first add some extra variables to the controller, and some new methods on to the scope. These should be self-descriptive.
ListController = function($scope, itemService) {
    var pagesShown = 1;
    var pageSize = 5;
    $scope.items = itemService.getAll();
    $scope.itemsLimit = function() {
        return pageSize * pagesShown;
    };
    $scope.hasMoreItemsToShow = function() {
        return pagesShown < ($scope.items.length / pageSize);
    };
    $scope.showMoreItems = function() {
        pagesShown = pagesShown + 1;         
    };
};​
We can now make use of those new methods on the scope in our view. 
<div ng-app="module" ng-controller="ListController">
    <ul>
       <li ng-repeat="item in items | limitTo: itemsLimit()">
          {{ item }}        
       </li>               
    </ul>
    <button ng-show="hasMoreItemsToShow()" ng-click="showMoreItems()">Show more</button>    
</div>
We made use of directives to add behaviour to the 'Show more' button and used the limitTo filter to limit the number of items rendered. Filters are used to format display data.

Here is the full jsFiddle.

I'm pretty sure you can abstract paging into a reusable component, but I thought this scenario shows a good bunch of basic Angular concepts.

So what are your thoughts on Angular? Pretty slick, right? Or are you already using Angular?

7 comments:

  1. Nice writeup, but your code doesn't page, it incrementally adds items. Pageing would show a moving cursor of items, not just an ever increasing list of items.

    ReplyDelete
    Replies
    1. Thank you for your comment.

      Some see this technique as "endless" pagination (http://www.codinghorror.com/blog/2012/03/the-end-of-pagination.html).

      Delete
  2. Nice article :)

    We can also submit our .net related article links on http://www.dotnettechy.com/ to increase traffic

    ReplyDelete
  3. this implementation would work for cases where you are getting your data in your code. Say you have to do a call to a server and use promise say for example

    module.factory('itemService', function($http) {
    return {
    getAll : function(url) {
    var items = $http.get(url).then(function(response){
    return response.data
    });

    return items;
    }
    };
    });

    by the time you call

    $scope.hasMoreItemsToShow = function() {
    return pagesShown < ($scope.items.length / pageSize);
    };

    $scope.items.length would be undefined. Is there anyway around this?

    ReplyDelete
    Replies
    1. You just have to make a copy on your controller

      Example of code from my service:

      //get orders method returns orders as a collection
      getOrders: function() {
      var deferred = $q.defer();
      $http.get(xo.config.apiPath).success(function (data) {
      deferred.resolve(data);
      }).error(function() {
      deferred.reject();
      });
      return deferred.promise;
      },

      And this is how I call this service on my controller:

      orderStatusService.getOrders().then(function(orders) {
      $scope.orders = orders;
      });

      Delete
  4. nice article in paging.

    Have any idea about paging with page number link as when you click the page number, we load data from server.

    ReplyDelete
  5. Good one .
    I have tried to explain the same here along with simple client side pagintion

    http://angulartutorial.blogspot.in/2014/04/angular-js-client-side-show-more.html

    Do you know how to do pagination same like google using angular .Or any reference link

    ReplyDelete