Как сделать пейджинг в AngularJS?
У меня есть набор данных около 1000 элементов в памяти и я пытаюсь создать пейджер для этот набор данных, но я не уверен, как это сделать.
Я использую функцию пользовательского фильтра для фильтрации результатов, и это работает нормально, но как-то мне нужно получить количество страниц.
какие-то зацепки?
21 ответ:
угловой пользовательский интерфейс начальной загрузки - директива пагинации
проверить UI Bootstrap ' s директива разбиения на страницы. Я закончил тем, что использовал его, а не то, что опубликовано здесь, поскольку у него достаточно функций для моего текущего использования и есть тщательный тест spec, чтобы сопровождать его.
View
<!-- table here --> <pagination ng-model="currentPage" total-items="todos.length" max-size="maxSize" boundary-links="true"> </pagination> <!-- items/page select here if you like -->
контроллер
todos.controller("TodoController", function($scope) { $scope.filteredTodos = [] ,$scope.currentPage = 1 ,$scope.numPerPage = 10 ,$scope.maxSize = 5; $scope.makeTodos = function() { $scope.todos = []; for (i=1;i<=1000;i++) { $scope.todos.push({ text:"todo "+i, done:false}); } }; $scope.makeTodos(); $scope.$watch("currentPage + numPerPage", function() { var begin = (($scope.currentPage - 1) * $scope.numPerPage) , end = begin + $scope.numPerPage; $scope.filteredTodos = $scope.todos.slice(begin, end); }); });
Я рабочая plunker для справки.
наследие Версия:
View
<!-- table here --> <div data-pagination="" data-num-pages="numPages()" data-current-page="currentPage" data-max-size="maxSize" data-boundary-links="true"></div> <!-- items/page select here if you like -->
контроллер
todos.controller("TodoController", function($scope) { $scope.filteredTodos = [] ,$scope.currentPage = 1 ,$scope.numPerPage = 10 ,$scope.maxSize = 5; $scope.makeTodos = function() { $scope.todos = []; for (i=1;i<=1000;i++) { $scope.todos.push({ text:"todo "+i, done:false}); } }; $scope.makeTodos(); $scope.numPages = function () { return Math.ceil($scope.todos.length / $scope.numPerPage); }; $scope.$watch("currentPage + numPerPage", function() { var begin = (($scope.currentPage - 1) * $scope.numPerPage) , end = begin + $scope.numPerPage; $scope.filteredTodos = $scope.todos.slice(begin, end); }); });
Я рабочая plunker для справки.
недавно я реализовал подкачку для построенного с угловым сайтом. Вы Чан проверить источник:https://github.com/angular/builtwith.angularjs.org
Я бы не стал использовать фильтр для разделения страниц. Вы должны разбить элементы на страницы внутри контроллера.
мне приходилось реализовывать пагинацию довольно много раз с угловым, и это всегда было немного больно за то, что я чувствовал, что можно упростить. Я использовал некоторые из идей, представленных здесь и в других местах, чтобы сделать модуль разбиения на страницы, который делает разбиение на страницы так же просто, как:
<ul> <li dir-paginate="item in items | itemsPerPage: 10">{{ item }}</li> </ul> // then somewhere else on the page .... <dir-pagination-controls></dir-pagination-controls>
вот и все. Он имеет следующие особенности:
- нет пользовательского кода, необходимого в контроллере, чтобы связать коллекцию
items
к ссылкам разбиения на страницы.- вы не обязательно использовать таблицу или gridview - вы можете разбить на страницы все, что вы можете ng-повторить!
- делегатов
ng-repeat
, Так что вы можете использовать любое выражение, которое может быть достоверно использованы вng-repeat
, включая фильтровать, приказывать etc.- работает через контроллеры-the
Я только что сделал JSFiddle, который показывает разбиение на страницы + поиск + порядок по каждому столбцу с помощью кода btford: http://jsfiddle.net/SAWsA/11/
я обновил Scotty.NET ' s plunkr http://plnkr.co/edit/FUeWwDu0XzO51lyLAEIA?p=preview так что он использует более новые версии angular, angular-ui и bootstrap.
контроллер
var todos = angular.module('todos', ['ui.bootstrap']); todos.controller('TodoController', function($scope) { $scope.filteredTodos = []; $scope.itemsPerPage = 30; $scope.currentPage = 4; $scope.makeTodos = function() { $scope.todos = []; for (i=1;i<=1000;i++) { $scope.todos.push({ text:'todo '+i, done:false}); } }; $scope.figureOutTodosToDisplay = function() { var begin = (($scope.currentPage - 1) * $scope.itemsPerPage); var end = begin + $scope.itemsPerPage; $scope.filteredTodos = $scope.todos.slice(begin, end); }; $scope.makeTodos(); $scope.figureOutTodosToDisplay(); $scope.pageChanged = function() { $scope.figureOutTodosToDisplay(); }; });
Bootstrap UI component
<pagination boundary-links="true" max-size="3" items-per-page="itemsPerPage" total-items="todos.length" ng-model="currentPage" ng-change="pageChanged()"></pagination>
это чистое решение javascript, которое я обернул как угловой сервис для реализации логики разбиения на страницы, как в результатах поиска google.
рабочая демонстрация на CodePen в http://codepen.io/cornflourblue/pen/KVeaQL/
подробности и объяснения в этот блог
function PagerService() { // service definition var service = {}; service.GetPager = GetPager; return service; // service implementation function GetPager(totalItems, currentPage, pageSize) { // default to first page currentPage = currentPage || 1; // default page size is 10 pageSize = pageSize || 10; // calculate total pages var totalPages = Math.ceil(totalItems / pageSize); var startPage, endPage; if (totalPages <= 10) { // less than 10 total pages so show all startPage = 1; endPage = totalPages; } else { // more than 10 total pages so calculate start and end pages if (currentPage <= 6) { startPage = 1; endPage = 10; } else if (currentPage + 4 >= totalPages) { startPage = totalPages - 9; endPage = totalPages; } else { startPage = currentPage - 5; endPage = currentPage + 4; } } // calculate start and end item indexes var startIndex = (currentPage - 1) * pageSize; var endIndex = startIndex + pageSize; // create an array of pages to ng-repeat in the pager control var pages = _.range(startPage, endPage + 1); // return object with all pager properties required by the view return { totalItems: totalItems, currentPage: currentPage, pageSize: pageSize, totalPages: totalPages, startPage: startPage, endPage: endPage, startIndex: startIndex, endIndex: endIndex, pages: pages }; } }
я извлек соответствующие биты здесь. Это табличный пейджер "без излишеств", поэтому сортировка или фильтрация не включены. Не стесняйтесь изменять/добавлять по мере необходимости:
//your data source may be different. the following line is //just for demonstration purposes only var modelData = [{ text: 'Test1' }, { text: 'Test2' }, { text: 'Test3' }]; (function(util) { util.PAGE_SIZE = 10; util.range = function(start, end) { var rng = []; if (!end) { end = start; start = 0; } for (var i = start; i < end; i++) rng.push(i); return rng; }; util.Pager = function(data) { var self = this, _size = util.PAGE_SIZE;; self.current = 0; self.content = function(index) { var start = index * self.size, end = (index * self.size + self.size) > data.length ? data.length : (index * self.size + self.size); return data.slice(start, end); }; self.next = function() { if (!self.canPage('Next')) return; self.current++; }; self.prev = function() { if (!self.canPage('Prev')) return; self.current--; }; self.canPage = function(dir) { if (dir === 'Next') return self.current < self.count - 1; if (dir === 'Prev') return self.current > 0; return false; }; self.list = function() { var start, end; start = self.current < 5 ? 0 : self.current - 5; end = self.count - self.current < 5 ? self.count : self.current + 5; return Util.range(start, end); }; Object.defineProperty(self, 'size', { configurable: false, enumerable: false, get: function() { return _size; }, set: function(val) { _size = val || _size; } }); Object.defineProperty(self, 'count', { configurable: false, enumerable: false, get: function() { return Math.ceil(data.length / self.size); } }); }; })(window.Util = window.Util || {}); (function(ns) { ns.SampleController = function($scope, $window) { $scope.ModelData = modelData; //instantiate pager with array (i.e. our model) $scope.pages = new $window.Util.Pager($scope.ModelData); }; })(window.Controllers = window.Controllers || {});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <table ng-controller="Controllers.SampleController"> <thead> <tr> <th> Col1 </th> </tr> </thead> <tbody> <tr ng-repeat="item in pages.content(pages.current)" title="{{item.text}}"> <td ng-bind-template="{{item.text}}"></td> </tr> </tbody> <tfoot> <tr> <td colspan="4"> <a href="#" ng-click="pages.prev()">«</a> <a href="#" ng-repeat="n in pages.list()" ng-click="pages.current = n" style="margin: 0 2px;">{{n + 1}}</a> <a href="#" ng-click="pages.next()">»</a> </td> </tr> </tfoot> </table>
мобильный Угловой адаптер jQuery имеет фильтр подкачки, который вы можете использовать.
вот демо-скрипка, которая использует его (добавьте более 5 элементов, и он становится постраничным):http://jsfiddle.net/tigbro/Du2DY/
вот источник: https://github.com/tigbro/jquery-mobile-angular-adapter/blob/master/src/main/webapp/utils/paging.js
Я использую эту стороннюю библиотеку разбиения на страницы, и она хорошо работает. Он может делать локальные / удаленные источники данных, и это очень настраивается.
https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination
<dir-pagination-controls [max-size=""] [direction-links=""] [boundary-links=""] [on-page-change=""] [pagination-id=""] [template-url=""] [auto-hide=""]> </dir-pagination-controls>
ниже решение довольно простое.
<pagination total-items="totalItems" items-per-page= "itemsPerPage" ng-model="currentPage" class="pagination-sm"> </pagination> <tr ng-repeat="country in countries.slice((currentPage -1) * itemsPerPage, currentPage * itemsPerPage) ">
для тех, кто считает, что это трудно, как я, чтобы создать пагинатор для таблицы я публикую это. Итак, на ваш взгляд :
<pagination total-items="total" items-per-page="itemPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination> <!-- To specify your choice of items Per Pages--> <div class="btn-group"> <label class="btn btn-primary" ng-model="radioModel" btn-radio="'Left'" data-ng-click="setItems(5)">5</label> <label class="btn btn-primary" ng-model="radioModel" btn-radio="'Middle'" data-ng-click="setItems(10)">10</label> <label class="btn btn-primary" ng-model="radioModel" btn-radio="'Right'" data-ng-click="setItems(15)">15</label> </div> //And don't forget in your table: <tr data-ng-repeat="p in profiles | offset: (currentPage-1)*itemPerPage | limitTo: itemPerPage" >
в вашем angularJs:
var module = angular.module('myapp',['ui.bootstrap','dialogs']); module.controller('myController',function($scope,$http){ $scope.total = $scope.mylist.length; $scope.currentPage = 1; $scope.itemPerPage = 2; $scope.start = 0; $scope.setItems = function(n){ $scope.itemPerPage = n; }; // In case you can replace ($scope.currentPage - 1) * $scope.itemPerPage in <tr> by "start" $scope.pageChanged = function() { $scope.start = ($scope.currentPage - 1) * $scope.itemPerPage; }; }); //and our filter module.filter('offset', function() { return function(input, start) { start = parseInt(start, 10); return input.slice(start); }; });
ng-повторите разбиение на страницы
<div ng-app="myApp" ng-controller="MyCtrl"> <input ng-model="q" id="search" class="form-control" placeholder="Filter text"> <select ng-model="pageSize" id="pageSize" class="form-control"> <option value="5">5</option> <option value="10">10</option> <option value="15">15</option> <option value="20">20</option> </select> <ul> <li ng-repeat="item in data | filter:q | startFrom:currentPage*pageSize | limitTo:pageSize"> {{item}} </li> </ul> <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1"> Previous </button> {{currentPage+1}}/{{numberOfPages()}} <button ng-disabled="currentPage >= getData().length/pageSize - 1" ng- click="currentPage=currentPage+1"> Next </button> </div> <script> var app=angular.module('myApp', []); app.controller('MyCtrl', ['$scope', '$filter', function ($scope, $filter) { $scope.currentPage = 0; $scope.pageSize = 10; $scope.data = []; $scope.q = ''; $scope.getData = function () { return $filter('filter')($scope.data, $scope.q) } $scope.numberOfPages=function(){ return Math.ceil($scope.getData().length/$scope.pageSize); } for (var i=0; i<65; i++) { $scope.data.push("Item "+i); } }]); app.filter('startFrom', function() { return function(input, start) { start = +start; //parse to int return input.slice(start); } }); </script>
предыдущие сообщения рекомендовали в основном, как построить подкачку самостоятельно. Если вы похожи на меня и предпочитаете готовую директиву, я только что нашел отличную под названием ngTable. Он поддерживает сортировку, фильтрацию и разбиение на страницы.
это очень чистое решение, все, что вам нужно в вашем представлении:
<table ng-table="tableParams" class="table"> <tr ng-repeat="user in $data"> <td data-title="'Name'" sortable="'name'"> {{user.name}} </td> <td data-title="'Age'" sortable="'age'"> {{user.age}} </td> </tr> </table>
и в контроллер:
$scope.tableParams = new ngTableParams({ page: 1, // show first page count: 10, // count per page sorting: { name: 'asc' // initial sorting } }, { total: data.length, // length of data getData: function($defer, params) { // use build-in angular filter var orderedData = params.sorting() ? $filter('orderBy')(data, params.orderBy()) : data; var start = (params.page() - 1) * params.count(); var end = params.page() * params.count(); $defer.resolve(orderedData.slice( start, end)); } });
ссылка на GitHub: https://github.com/esvit/ng-table/
вот мой пример. Выбранная кнопка в середине списка Контроллер. конфигурация >>>
$scope.pagination = {total: null, pages: [], config: {count: 10, page: 1, size: 7}};
логика разбиения на страницы:
/* Pagination */ $scope.$watch('pagination.total', function (total) { if(!total || total <= $scope.pagination.config.count) return; _setPaginationPages(total); }); function _setPaginationPages(total) { var totalPages = Math.ceil(total / $scope.pagination.config.count); var pages = []; var start = $scope.pagination.config.page - Math.floor($scope.pagination.config.size/2); var finish = null; if((start + $scope.pagination.config.size - 1) > totalPages){ start = totalPages - $scope.pagination.config.size; } if(start <= 0) { start = 1; } finish = start + $scope.pagination.config.size - 1; if(finish > totalPages){ finish = totalPages; } for (var i = start; i <= finish; i++) { pages.push(i); } $scope.pagination.pages = pages; } $scope.$watch("pagination.config.page", function(page){ _setPaginationPages($scope.pagination.total); _getRespondents($scope.pagination.config); });
и мой взгляд на bootstap
<ul ng-class="{hidden: pagination.total == 0}" class="pagination"> <li ng-click="pagination.config.page = pagination.config.page - 1" ng-class="{disabled: pagination.config.page == 1}" ><a href="#">«</a></li> <li ng-repeat="p in pagination.pages" ng-click="pagination.config.page = p" ng-class="{active: p == pagination.config.page}"><a href="#">{{p}}</a></li> <li ng-click="pagination.config.page = pagination.config.page + 1" ng-class="{disabled: pagination.config.page == pagination.pages.length}"><a href="#">»</a></li> </ul >
Это полезно
- Это прекрасный выбор
директива для помощи в разбиении на страницы больших наборов данных, требуя при этом минимум фактической информации о разбиении на страницы. Мы очень зависим от сервера для "фильтрации" результатов в этой схеме подкачки. Основная идея заключается в том, что мы хотим только держать активную "страницу" элементов, а не держать весь список элементов в памяти и подкачки на стороне клиента.
старый вопрос, но поскольку я думаю, что мой подход немного отличается и менее сложный, я поделюсь этим и надеюсь, что кто-то кроме меня найдет его полезным.
что я нашел, чтобы быть легкий и компактный для разбиения на страницы необходимо объединить директиву с фильтром, который использует те же переменные области.
для реализации этого вы добавляете фильтр на массив и добавить directiv, как это
<div class="row"> <table class="table table-hover"> <thead> <tr> <th>Name</th> <th>Price</th> <th>Quantity</th> </tr> </thead> <tbody> <tr ng-repeat="item in items | cust_pagination:p_Size:p_Step"> <td>{{item.Name}}</td> <td>{{item.Price}}</td> <td>{{item.Quantity}}</td> </tr> </tbody> </table> <div cust-pagination p-items="items" p-boundarylinks="true" p-size="p_Size" p-step="p_Step"></div> </div>
p_size и p_Step-это переменные области, которые могут быть настроенным в области else значение по умолчанию p_Size равно 5, а p_Step-1.
при изменении шага в разбивке на страницы p_step обновляется и запускает новую фильтрацию с помощью фильтра cust_pagination. Затем фильтр cust_pagination срезает массив в зависимости от значения p_Step, как показано ниже, и возвращает только активные записи, выбранные в разделе разбиения на страницы
var startIndex = nStep * nPageSize; var endIndex = startIndex + nPageSize; var arr = items.slice(startIndex, endIndex); return arr;
Я хотел бы прокомментировать, но мне просто нужно оставить это здесь:
Scotty.NET ответ и повтор user2176745 для более поздних версий оба великолепны, но они оба пропускают что - то, что моя версия AngularJS (v1.3.15) ломается:
i не определен в $scope.макетодос.
таким образом, замена этой функцией исправляет ее для более поздних угловых версий.
$scope.makeTodos = function() { var i; $scope.todos = []; for (i=1;i<=1000;i++) { $scope.todos.push({ text:'todo '+i, done:false}); } };
обзор : разбиение на страницы с помощью
- ng-repeat - uib-pagination
View :
<div class="row"> <div class="col-lg-12"> <table class="table"> <thead style="background-color: #eee"> <tr> <td>Dispature</td> <td>Service</td> <td>Host</td> <td>Value</td> </tr> </thead> <tbody> <tr ng-repeat="x in app.metricsList"> <td>{{x.dispature}}</td> <td>{{x.service}}</td> <td>{{x.host}}</td> <td>{{x.value}}</td> </tr> </tbody> </table> <div align="center"> <uib-pagination items-per-page="app.itemPerPage" num-pages="numPages" total-items="app.totalItems" boundary-link-numbers="true" ng-model="app.currentPage" rotate="false" max-size="app.maxSize" class="pagination-sm" boundary-links="true" ng-click="app.getPagableRecords()"></uib-pagination> <div style="float: right; margin: 15px"> <pre>Page: {{app.currentPage}} / {{numPages}}</pre> </div> </div> </div> </div>
JS контроллер :
app.controller('AllEntryCtrl',['$scope','$http','$timeout','$rootScope', function($scope,$http,$timeout,$rootScope){ var app = this; app.currentPage = 1; app.maxSize = 5; app.itemPerPage = 5; app.totalItems = 0; app.countRecords = function() { $http.get("countRecord") .success(function(data,status,headers,config){ app.totalItems = data; }) .error(function(data,status,header,config){ console.log(data); }); }; app.getPagableRecords = function() { var param = { page : app.currentPage, size : app.itemPerPage }; $http.get("allRecordPagination",{params : param}) .success(function(data,status,headers,config){ app.metricsList = data.content; }) .error(function(data,status,header,config){ console.log(data); }); }; app.countRecords(); app.getPagableRecords(); }]);
вы можете легко сделать это с помощью директивы Bootstrap UI.
этот ответ является модификацией ответа, данного @Scotty.NET, я изменил код, потому что
<pagination>
директива теперь устарела.следующий код генерирует разбиение на страницы:
<ul uib-pagination boundary-links="true" total-items="totalItems" items-per-page="itemsPerPage" ng-model="currentPage" ng-change="pageChanged()" class="pagination" previous-text="‹" next-text="›" first-text="«" last-text="»"></ul>
чтобы сделать его функциональным, используйте это в своем контроллере:
$scope.filteredData = [] $scope.totalItems = $scope.data.length; $scope.currentPage = 1; $scope.itemsPerPage = 5; $scope.setPage = function (pageNo) { $scope.currentPage = pageNo; }; $scope.pageChanged = function() { var begin = (($scope.currentPage - 1) * $scope.itemsPerPage) , end = begin + $scope.itemsPerPage; $scope.filteredData = $scope.data.slice(begin, end); }; $scope.pageChanged();
см. это для получения дополнительных параметров разбиения на страницы:Bootstrap UI Pagination директива
Я хотел бы добавить мое решение, которое работает с
ngRepeat
и фильтров, которые вы используете с ним без использования$watch
или нарезанный массив.ваши результаты фильтра будут разбиты на страницы!
var app = angular.module('app', ['ui.bootstrap']); app.controller('myController', ['$scope', function($scope){ $scope.list= ['a', 'b', 'c', 'd', 'e']; $scope.pagination = { currentPage: 1, numPerPage: 5, totalItems: 0 }; $scope.searchFilter = function(item) { //Your filter results will be paginated! //The pagination will work even with other filters involved //The total number of items in the result of your filter is accounted for }; $scope.paginationFilter = function(item, index) { //Every time the filter is used it restarts the totalItems if(index === 0) $scope.pagination.totalItems = 0; //This holds the totalItems after the filters are applied $scope.pagination.totalItems++; if( index >= (($scope.pagination.currentPage - 1) * $scope.pagination.numPerPage) && index < ((($scope.pagination.currentPage - 1) * $scope.pagination.numPerPage) + $scope.pagination.numPerPage) ) return true; //return true if item index is on the currentPage return false; }; }]);
в HTML убедитесь, что вы применяете свои фильтры к
ngRepeat
до фильтр разбиения на страницы.<table data-ng-controller="myController"> <tr data-ng-repeat="item in list | filter: searchFilter | filter: paginationFilter track by $index"> <td> {{item}} </td> <tr> </table> <ul class="pagination-sm" uib-pagination data-boundary-links="true" data-total-items="pagination.totalItems" data-items-per-page="pagination.numPerPage" data-ng-model="pagination.currentPage" data-previous-text="‹" data-next-text="›" data-first-text="«" data-last-text="»"> </ul>