Using data binders and clever event handling this can be easily accomplished by angular and the result is incredibly satisfying. Let me show you the code. First add these stylesheets:
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap.no-icons.min.css" rel="stylesheet"> <link href="http://netdna.bootstrapcdn.com/font-awesome/2.0/css/font-awesome.css" rel="stylesheet">Now the table view. It binds any change to the text input to the function $scope.search(), adds all the pagedItems[currentPage] array to the table's body and sets up the pagination logic in the table's foot:
<div ng-controller="ctrlRead"> <div class="input-append"> <input type="text" ng-model="query" ng-change="search()" class="input-large search-query" placeholder="Search"> <span class="add-on"><i class="icon-search"></i></span> </div> <table class="table table-striped table-condensed table-hover"> <thead> <tr> <th class="id">Id </th> <th class="name">Name </th> <th class="description">Description </th> <th class="field3">Field 3 </th> <th class="field4">Field 4 </th> <th class="field5">Field 5 </th> </tr> </thead> <tfoot> <td colspan="6"> <div class="pagination pull-right"> <ul> <li ng-class="{disabled: currentPage == 0}"> <a href ng-click="prevPage()">« Prev</a> </li> <li ng-repeat="n in range(pagedItems.length)" ng-class="{active: n == currentPage}" ng-click="setPage()"> <a href ng-bind="n + 1">1</a> </li> <li ng-class="{disabled: currentPage == pagedItems.length - 1}"> <a href ng-click="nextPage()">Next »</a> </li> </ul> </div> </td> </tfoot> <tbody> <tr ng-repeat="item in pagedItems[currentPage] | orderBy:sortingOrder:reverse"> <td>{{item.id}}</td> <td>{{item.name}}</td> <td>{{item.description}}</td> <td>{{item.field3}}</td> <td>{{item.field4}}</td> <td>{{item.field5}}</td> </tr> </tbody> </table> </div>We need only build the filteredItems array after each call to $scope.search() accordingly. This can be done by passing the classic needle/haystack function as $filter's matching function and then split the result based on the number of elements per page. But talk is cheap, I know:
//classical way of finding a substring (needle) in a given string (haystack) var searchMatch = function (haystack, needle) { if (!needle) { return true; } return haystack.toLowerCase().indexOf(needle.toLowerCase()) !== -1; }; // filter the items following the search string $scope.search = function () { $scope.filteredItems = $filter('filter')($scope.items, function (item) { for(var attr in item) { if(item.hasOwnProperty(attr)) if (searchMatch(item[attr], $scope.query)) return true; } return false; }); // take care of the sorting order if ($scope.sortingOrder !== '') { $scope.filteredItems = $filter('orderBy')($scope.filteredItems, $scope.sortingOrder, $scope.reverse); } $scope.currentPage = 0; // now group by pages $scope.groupToPages(); }; // divide elements by page $scope.groupToPages = function () { $scope.pagedItems = []; for (var i = 0; i < $scope.filteredItems.length; i++) { if (i % $scope.itemsPerPage === 0) { $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)] = [ $scope.filteredItems[i] ]; } else { $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)].push($scope.filteredItems[i]); } } };Finally, just add the paging logic for the table's foot and call $scope.search() so we show everything after the page loads.
$scope.range = function (end) { var ret = []; for(var i = 0; i < end; i++) { ret.push(i); } return ret; }; $scope.prevPage = function () { if ($scope.currentPage > 0) { $scope.currentPage--; } }; $scope.nextPage = function () { if ($scope.currentPage < $scope.pagedItems.length - 1) { $scope.currentPage++; } }; $scope.setPage = function () { $scope.currentPage = this.n; }; // create filtered items for the first time $scope.search();Just add this code to our view's controller and we're done! Check out this fiddle with our working code and some mock data! Now, of course this is only useful when all the necessary data can be fetched from the server quickly, so that the search is consistent. If you cannot provide Angular with all the necessary data, perhaps you should consider infinite scrolling instead. If that's the case, check this out. Have fun!
ftw.