var app = angular.module('plunker', ['xui.table','ngMaterial']);

app.constant('TableHeader', [{
    header: 'id',
    title: 'id',
    sortable: true
}, {
    header: 'name',
    title: 'name',
    sortable: true
}, {
    header: 'description',
    title: 'description',
    sortable: true
}, {
    header: 'field3',
    title: 'field3',
    sortable: true
}, {
    header: 'field4',
    title: 'field4',
    sortable: true
}, {
    header: 'Action',
    title: 'Action',
    sortable: false
}

]);
app.controller('MainCtrl', function($scope, TableHeader) {
    $scope.name = 'World';
    $scope.TableHeader = TableHeader;
    $scope.List = [];
    for (var i = 1; i < 65; i++) {
        $scope.List.push({
            "id": "0" + i,
            "name": "name " + i,
            "description": "description " + i,
            "field3": "field3 " + i,
            "field4": true,
            "field5 ": "field5 " + i
        });
    }
    $scope.search = {};

    $scope.searchfn = function(input) {

        if ( !! input) {
            var tmp = false;
            angular.forEach($scope.search, function(val, key) {
                if ( !! (input[key].indexOf(val || '') !== -1)) {
                    tmp = true;
                }
            });
            return tmp;
        }
    };
    $scope.open = function(input) {
        alert(input);
    };
});
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>
        document.write('<base href="' + document.location + '" />');
    </script>
    <link data-require="angular-material.min.css" rel="Stylesheet" href="https://rawgit.com/angular/bower-material/v0.7.0-rc2-master-d6311e8/angular-material.min.css" data-semver="v0.7.0-rc2-master-d6311e8" />
    <link rel="stylesheet" href="style.css" />
    <link data-require="bootstrap-css" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" data-semver="3.3.1" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.8/angular.js" data-semver="1.3.8"></script>
    <script src="pagination.js"></script>
    <script src="xuiTable.js"></script>
    <script data-require="hammer.js@2.0.x" src="https://rawgit.com/hammerjs/hammer.js/2.0.4/hammer.min.js" data-semver="2.0.4"></script>
    <script data-require="angular-material.min.js@v0.7.0-rc2-master-d6311e8" src="https://rawgit.com/angular/bower-material/v0.7.0-rc2-master-d6311e8/angular-material.min.js" data-semver="v0.7.0-rc2-master-d6311e8"></script>
    <script data-require="angular-aria.min.js@1.3.8" src="https://rawgit.com/angular/bower-angular-aria/v1.3.8/angular-aria.min.js" data-semver="1.3.8"></script>
    <script data-require="angular-animate.min.js@1.3.8" src="https://rawgit.com/angular/bower-angular-animate/v1.3.8/angular-animate.min.js" data-semver="1.3.8"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>
    <div>
      <a href="https://github.com/maxisam/Angular-XUI/tree/master/xuiTable">GitHub</a>
    </div>
    <input type="text" ng-model="PageSize" placeholder="page size" />
    <input type="text" ng-model="search.id" placeholder="id" />
    <input type="text" ng-model="search.description" placeholder="description" />
    <table xui-table="test" table-headers="TableHeader" table-data="List" items-per-page="10" page-window="PageSize" table-pager="true" table-filter="searchfn(input)" table-filter-factor="search">
      <tbody>
        <tr>
          <td class="id" data-fileid="{{row.id}}">{{row.id}} </td>
          <td class="name">{{row.name }} </td>
          <td class="description">{{row.description}} </td>
          <td class="field3">{{row.field3}} </td>
          <td class="field4">
            <md-checkbox class="field4" ng-model="row.field4" aria-label="Selected"></md-checkbox>
          </td>
          <td class="Action">
            <button ng-click="$parent.$parent.open(row.id)">test</button>
          </td>
        </tr>
      </tbody>
    </table>
  </body>

</html>
/* Put your css in here */

.sort-true {
  padding-right: 14px;
  background: no-repeat right center url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAICAYAAAAvOAWIAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTAw9HKhAAAAX0lEQVQoU32NQQrAMAgEBa+CvfX5PYa+pn/oR2w2xLIQibAgOqMSEYe73wh6oVp2Ztb6PhAWEszd5OTJAQknRJ73Hpz4bMZ1RFXfAgQ3ahEIxsUf3AklWAlbkIWrev0BT3ZAxrSn1UcAAAAASUVORK5CYII=')
  /*../Icons/Icon-down-white.png*/
}
.sort-false {
  padding-right: 14px;
  background: no-repeat right center url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAICAYAAAAvOAWIAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTAw9HKhAAAAXklEQVQoU2NgwAT8QKH5QAyi8QKQgvNA/B9K49SArBCkGKcGbAqxasBQyMzM/AhqMoYGmBvBEvz8/Cv///8vDqIxNPDw8CyHCUIVCoCCAKhBAFkDWB1MEFkhLLzQ5QBl6UDG/jPuNgAAAABJRU5ErkJggg==')
  /*../Icons/Icon-up-white.png*/
}
.NoData td.NoData {
  text-align: center;
}
.even {
  background-color: #aaa
}
.odd {
  background-color: #fff;
}
.icon {
  max-height: 20px;
  max-width: 20px;
}
.SelectedRow {
  background-color: #888;
}
/**
 * Created by linsz on 3/14/14.
 */
angular.module('xui.table', ['ui.bootstrap.pagination'])
  .controller('xuiTableCtrl', ['$scope', '$attrs', 'orderByFilter', '$filter', '$parse',
    function($scope, $attrs, orderByFilter, $filter, $parse) {

      var attrs = ['tableHeaders', 'tableData', 'itemsPerPage', 'pageWindow', 'tableFilterFactor'];
      angular.forEach(attrs, function(val) {
        var tmp = $parse($attrs[val]);
        $scope[val] = tmp($scope.$parent);
        $scope.$parent.$watch(tmp, function(newVal) {
          $scope[val] = tmp($scope.$parent);
        });
      });

      $attrs.$observe('tablePager', function(val) {
        $scope.tablePager = val;
      });

      $scope.tableFilter = function(locals) {
        return $parse($attrs.tableFilter)($scope.$parent, locals);
      };

      var that = this;
      $scope.Page = {
        current: 1
      };
      $scope.sort = {
        column: '',
        descending: false
      };
      $scope.SelectedCol = function(column) {
        return column == $scope.sort.column && 'sort-' + $scope.sort.descending;
      };
      $scope.ChangeSorting = function(column) {
        if ($scope.sort.column == column) {
          $scope.sort.descending = !$scope.sort.descending;
        } else {
          $scope.sort.column = column.toString();
          $scope.sort.descending = false;
        }
        $scope.tableDataFiltered = orderByFilter($scope.tableDataFiltered, $scope.sort.column, $scope.sort.descending);
        that.pagination();
      };

      this.pagination = function() {
        $scope.pagedItems = [];
        if ( !! $scope.tablePager && angular.lowercase($scope.tablePager) === 'true') {
          for (var i = 0; i < $scope.tableDataFiltered.length; i++) {
            if (i % $scope.itemsPerPage === 0) {
              $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)] = [$scope.tableDataFiltered[i]];
            } else {
              $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)].push($scope.tableDataFiltered[i]);
            }
          }
        } else {
          $scope.pagedItems[0] = $scope.tableDataFiltered;
        }

      };

      this.search = function() {
        if ( !! $scope.tableFilterFactor && !angular.equals($scope.tableFilterFactor, {}) && !! $scope.tableFilter) {
          $scope.tableDataFiltered = $filter('filter')($scope.tableData, function(input) {
            return $scope.tableFilter({
              input: input
            });
          });
        } else {
          $scope.tableDataFiltered = $scope.tableData;
        }
        that.pagination();
      };
      this.search();
      $scope.$watch('tableFilterFactor', function(newVal) {
        that.search();
      }, true);
    }
  ])
  .directive('xuiTable', ['$compile',
    function($compile) {
      return {
        scope: true,
        replace: true,
        transclude: true,
        templateUrl: 'xuiTable.html',
        controller: 'xuiTableCtrl',
        link: function(scope, elm, attrs, controller, transcludeFn) {
          if ( !! scope.tableDataFiltered.length) {
            var tbody = transcludeFn(scope, function(clone) {
              clone.children('tr').attr("ng-repeat", 'row in  pagedItems[Page.current-1]');
            });
            $compile(tbody)(scope, function(clone) {
              elm.addClass(attrs.xuiTable).append(clone);
            });
          }
        }
      };
    }
  ]);
<table class="table table-striped">
  <thead>
    <tr>
      <th class="{{header.header}}" data-ng-repeat="header in tableHeaders">
        <a data-ng-if="header.sortable" href='javascript:void(0)' data-ng-click="ChangeSorting(header.header)" data-ng-class="SelectedCol(header.header)">{{header.title}}</a>
        <span class="{{header.header}}" data-ng-if="!header.sortable">{{header.title}}</span>
      </th>
    </tr>
  </thead>
  <tbody data-ng-if="!tableDataFiltered.length">
    <tr>
      <td class="{{header.header}}" data-ng-repeat="header in tableHeaders">N/A</td>
    </tr>
  </tbody>
  <tfoot data-ng-if="!!tablePager">
    <td colspan="{{tableHeaders.length}}">
      <div class="pagination pull-right">
        <pagination total-items="tableDataFiltered.length" page="Page.current" max-size="pageWindow" class="pagination-sm" boundary-links="true"></pagination>
      </div>
    </td>
  </tfoot>
</table>
angular.module('ui.bootstrap.pagination', [])

.controller('PaginationController', ['$scope', '$attrs', '$parse', '$interpolate',
  function ($scope, $attrs, $parse, $interpolate) {
      var self = this,
        setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;

      this.init = function (defaultItemsPerPage) {
          if ($attrs.itemsPerPage) {
              $scope.$parent.$watch($parse($attrs.itemsPerPage), function (value) {
                  self.itemsPerPage = parseInt(value, 10);
                  $scope.totalPages = self.calculateTotalPages();
              });
          } else {
              this.itemsPerPage = defaultItemsPerPage;
          }
      };

      this.noPrevious = function () {
          return this.page === 1;
      };
      this.noNext = function () {
          return this.page === $scope.totalPages;
      };

      this.isActive = function (page) {
          return this.page === page;
      };

      this.calculateTotalPages = function () {
          var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
          return Math.max(totalPages || 0, 1);
      };

      this.getAttributeValue = function (attribute, defaultValue, interpolate) {
          return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
      };

      this.render = function () {
          this.page = parseInt($scope.page, 10) || 1;
          if (this.page > 0 && this.page <= $scope.totalPages) {
              $scope.pages = this.getPages(this.page, $scope.totalPages);
          }
      };

      $scope.selectPage = function (page) {
          if (!self.isActive(page) && page > 0 && page <= $scope.totalPages) {
              $scope.page = page;
              $scope.onSelectPage({
                  page: page
              });
          }
      };

      $scope.$watch('page', function () {
          self.render();
      });

      $scope.$watch('totalItems', function () {
          $scope.totalPages = self.calculateTotalPages();
      });

      $scope.$watch('totalPages', function (value) {
          setNumPages($scope.$parent, value); // Readonly variable

          if (self.page > value) {
              $scope.selectPage(value);
          } else {
              self.render();
          }
      });
  }
])

.constant('paginationConfig', {
    itemsPerPage: 10,
    boundaryLinks: false,
    directionLinks: true,
    firstText: 'First',
    previousText: 'Previous',
    nextText: 'Next',
    lastText: 'Last',
    rotate: true
})

.directive('pagination', ['$parse', 'paginationConfig',
  function ($parse, config) {
      return {
          restrict: 'EA',
          scope: {
              page: '=',
              totalItems: '=',
              onSelectPage: ' &'
          },
          controller: 'PaginationController',
          templateUrl: 'pagination.html',
          replace: true,
          link: function (scope, element, attrs, paginationCtrl) {

              // Setup configuration parameters
              var maxSize,
                boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks),
                directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks),
                firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
                previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
                nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
                lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
                rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);

              paginationCtrl.init(config.itemsPerPage);

              if (attrs.maxSize) {
                  scope.$parent.$watch($parse(attrs.maxSize), function (value) {
                      maxSize = parseInt(value, 10);
                      paginationCtrl.render();
                  });
              }

              // Create page object used in template
              function makePage(number, text, isActive, isDisabled) {
                  return {
                      number: number,
                      text: text,
                      active: isActive,
                      disabled: isDisabled
                  };
              }

              paginationCtrl.getPages = function (currentPage, totalPages) {
                  var pages = [];

                  // Default page limits
                  var startPage = 1,
                    endPage = totalPages;
                  var isMaxSized = (angular.isDefined(maxSize) && maxSize < totalPages);

                  // recompute if maxSize
                  if (isMaxSized) {
                      if (rotate) {
                          // Current page is displayed in the middle of the visible ones
                          startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
                          endPage = startPage + maxSize - 1;

                          // Adjust if limit is exceeded
                          if (endPage > totalPages) {
                              endPage = totalPages;
                              startPage = endPage - maxSize + 1;
                          }
                      } else {
                          // Visible pages are paginated with maxSize
                          startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;

                          // Adjust last page if limit is exceeded
                          endPage = Math.min(startPage + maxSize - 1, totalPages);
                      }
                  }

                  // Add page number links
                  for (var number = startPage; number <= endPage; number++) {
                      var page = makePage(number, number, paginationCtrl.isActive(number), false);
                      pages.push(page);
                  }

                  // Add links to move between page sets
                  if (isMaxSized && !rotate) {
                      if (startPage > 1) {
                          var previousPageSet = makePage(startPage - 1, '...', false, false);
                          pages.unshift(previousPageSet);
                      }

                      if (endPage < totalPages) {
                          var nextPageSet = makePage(endPage + 1, '...', false, false);
                          pages.push(nextPageSet);
                      }
                  }

                  // Add previous & next links
                  if (directionLinks) {
                      var previousPage = makePage(currentPage - 1, previousText, false, paginationCtrl.noPrevious());
                      pages.unshift(previousPage);

                      var nextPage = makePage(currentPage + 1, nextText, false, paginationCtrl.noNext());
                      pages.push(nextPage);
                  }

                  // Add first & last links
                  if (boundaryLinks) {
                      var firstPage = makePage(1, firstText, false, paginationCtrl.noPrevious());
                      pages.unshift(firstPage);

                      var lastPage = makePage(totalPages, lastText, false, paginationCtrl.noNext());
                      pages.push(lastPage);
                  }

                  return pages;
              };
          }
      };
  }
])

.constant('pagerConfig', {
    itemsPerPage: 10,
    previousText: '« Previous',
    nextText: 'Next »',
    align: true
})

.directive('pager', ['pagerConfig',
  function (config) {
      return {
          restrict: 'EA',
          scope: {
              page: '=',
              totalItems: '=',
              onSelectPage: ' &'
          },
          controller: 'PaginationController',
          templateUrl: 'template/pagination/pager.html',
          replace: true,
          link: function (scope, element, attrs, paginationCtrl) {

              // Setup configuration parameters
              var previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
                nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
                align = paginationCtrl.getAttributeValue(attrs.align, config.align);

              paginationCtrl.init(config.itemsPerPage);

              // Create page object used in template
              function makePage(number, text, isDisabled, isPrevious, isNext) {
                  return {
                      number: number,
                      text: text,
                      disabled: isDisabled,
                      previous: (align && isPrevious),
                      next: (align && isNext)
                  };
              }

              paginationCtrl.getPages = function (currentPage) {
                  return [
                    makePage(currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false),
                    makePage(currentPage + 1, nextText, paginationCtrl.noNext(), false, true)
                  ];
              };
          }
      };
  }
]);
<ul class="pagination">
    <li ng-repeat="page in pages" ng-class="{active: page.active, disabled: page.disabled}"><a ng-click="selectPage(page.number)">{{page.text}}</a></li>
</ul>