Paginación en una lista usando ng-repeat

130

Estoy tratando de agregar páginas a mi lista. Seguí el tutorial de AngularJS, el de los teléfonos inteligentes y estoy tratando de mostrar solo cierto número de objetos. Aquí está mi archivo html:

  <div class='container-fluid'>
    <div class='row-fluid'>
        <div class='span2'>
            Search: <input ng-model='searchBar'>
            Sort by: 
            <select ng-model='orderProp'>
                <option value='name'>Alphabetical</option>
                <option value='age'>Newest</option>
            </select>
            You selected the phones to be ordered by: {{orderProp}}
        </div>

        <div class='span10'>
          <select ng-model='limit'>
            <option value='5'>Show 5 per page</option>
            <option value='10'>Show 10 per page</option>
            <option value='15'>Show 15 per page</option>
            <option value='20'>Show 20 per page</option>
          </select>
          <ul class='phones'>
            <li class='thumbnail' ng-repeat='phone in phones | filter:searchBar | orderBy:orderProp | limitTo:limit'>
                <a href='#/phones/{{phone.id}}' class='thumb'><img ng-src='{{phone.imageUrl}}'></a>
                <a href='#/phones/{{phone.id}}'>{{phone.name}}</a>
                <p>{{phone.snippet}}</p>
            </li>
          </ul>
        </div>
    </div>
  </div>

Agregué una etiqueta de selección con algunos valores para limitar la cantidad de elementos que se mostrarán. Lo que quiero ahora es agregar la paginación para mostrar los próximos 5, 10, etc.

Tengo un controlador que funciona con esto:

function PhoneListCtrl($scope, Phone){
    $scope.phones = Phone.query();
    $scope.orderProp = 'age';
    $scope.limit = 5;
}

Y también tengo un módulo para recuperar los datos de los archivos json.

angular.module('phonecatServices', ['ngResource']).
    factory('Phone', function($resource){
        return $resource('phones/:phoneId.json', {}, {
            query: {method: 'GET', params:{phoneId:'phones'}, isArray:true}
        });
    });
Tomarto
fuente
1
Cuando dice que desea implementar la página siguiente y la página anterior, ¿desea que la paginación se realice únicamente en el lado del cliente o del servidor? Si el número de registros es demasiado alto, debe optar por la paginación del lado del servidor. En cualquier escenario, debe comenzar a mantener "startIndex" (el límite solo proporcionaría el número de registros en la página), aparte de esto, es necesario mantener la página actual de alguna manera, esto puede hacerse manteniendo startIndex.
Rutesh Makhijani
No tengo una gran cantidad de registros. Lo que quería hacer es usar el controlador que ya tengo (PhoneListCtrl). No sé si es del lado del servidor o del cliente. ¡Lo siento!
Tomarto
1
@RuteshMakhijani Tengo un requisito similar con un alto número de registros, explique la razón detrás del uso de la paginación del lado del servidor para un alto número de registros
Dinesh PR

Respuestas:

215

Si no tiene demasiados datos, definitivamente puede hacer paginación simplemente almacenando todos los datos en el navegador y filtrando lo que es visible en un momento determinado.

Aquí hay un ejemplo de paginación simple: http://jsfiddle.net/2ZzZB/56/

Ese ejemplo estaba en la lista de violines en el wiki angular.js github, que debería ser útil: https://github.com/angular/angular.js/wiki/JsFiddle-Examples

EDITAR: http://jsfiddle.net/2ZzZB/16/ a http://jsfiddle.net/2ZzZB/56/ (no mostrará "1 / 4.5" si hay 45 resultados)

Andrew Joslin
fuente
1
¡Muchas gracias! Es exactamente lo que estaba buscando. Vi ese ejemplo antes pero no funcionó. Ahora noté que hay un pequeño error de sintaxis. Falta un corchete después de la oración "for".
Tomarto
Agregué los corchetes y lo actualicé en la wiki. Pero debería haber funcionado sin ellos.
Andrew Joslin
1
¡Oh! no importa. Agrego una if (input?)condición antes de regresar input.slice(start), gracias Andy!
zx1986
1
Al igual que Bart, necesitaba pasar información de paginación a una función de llamada para obtener datos pagables; es similar pero diferente y podría ayudar en algunos casos. plnkr.co/edit/RcSso3verGtXwToilJ5a
Steve Black
3
Hola, esta demostración fue muy útil para un proyecto en el que me involucré. Necesitaba agregar la opción para ver todo o alternar la paginación y mostrar cada página. Entonces extendí la demo. muchas gracias. jsfiddle.net/juanmendez/m4dn2xrv
Juan Mendez
39

Acabo de hacer un JSFiddle que muestra la paginación + búsqueda + orden en cada columna usando el código de Build con Twitter Bootstrap : http://jsfiddle.net/SAWsA/11/

Spir
fuente
2
Trabajo asombroso. Sea bueno ir un paso más allá y hacer que los encabezados de columna sean dinámicos, es decir, obtenga todos los valores clave únicos de la matriz json de elementos y vincúlelos a una repetición ng en lugar de codificar los valores. Algo parecido a lo que hice aquí: jsfiddle.net/gavinfoley/t39ZP
GFoley83
teniendo todo ese código en el controlador sólo hace que sea menos reutilizable - telerik.com/help/silverlight/... Parece angular que falta: QueryableDomainServiceCollectionView, VirtualQueryableCollectionView, HierarchicalDataCollectionView
Leblanc Meneses
1
Esta es una solución realmente genial, fácil de adaptar. Lo usé en un escenario complejo donde la cantidad de dos elementos unidos había alcanzado los miles, y realmente no era una opción para refactorizar todo el código. Debe actualizar la respuesta con un código que explique los principios básicos. Y quizás actualizar AngularJS y versiones de archivos de inicio en el violín :)
davidkonrad
15

He creado un módulo que hace que la paginación en memoria sea increíblemente simple.

Le permite paginar simplemente reemplazando ng-repeatcon dir-paginate, especificando los elementos por página como un filtro canalizado, y luego soltando los controles donde desee en forma de una sola directiva,<dir-pagination-controls>

Para tomar el ejemplo original pedido por Tomarto, se vería así:

<ul class='phones'>
    <li class='thumbnail' dir-paginate='phone in phones | filter:searchBar | orderBy:orderProp | limitTo:limit | itemsPerPage: limit'>
            <a href='#/phones/{{phone.id}}' class='thumb'><img ng-src='{{phone.imageUrl}}'></a>
            <a href='#/phones/{{phone.id}}'>{{phone.name}}</a>
            <p>{{phone.snippet}}</p>
    </li>
</ul>

<dir-pagination-controls></dir-pagination-controls>

No hay necesidad de ningún código de paginación especial en su controlador. Todo es manejado internamente por el módulo.

Demostración: http://plnkr.co/edit/Wtkv71LIqUR4OhzhgpqL?p=preview

Fuente: dirPagination de GitHub

Michael Bromley
fuente
JFI: en lugar de dirPagination.tpl.html separado, también podemos incluir el código de paginación dentro de <dir-pagination-controls> <ul class = "pagination" ng-if = "1 <pages.length ||! AutoHide">. .. </ul> </dir-pagination-controls>
npcoder
5

Sé que este hilo es antiguo ahora, pero lo estoy respondiendo para mantener las cosas un poco actualizadas.

Con Angular 1.4 y superior, puede usar directamente limitTo filter que, además de aceptar el limitparámetro, también acepta un beginparámetro.

Uso: {{ limitTo_expression | limitTo : limit : begin}}

Por lo tanto, es posible que ahora no necesite usar ninguna biblioteca de terceros para lograr algo como la paginación. He creado un violín para ilustrar lo mismo.

Bharat Gupta
fuente
El violín al que se vinculó en realidad no usa el parámetro de inicio.
The Brawny Man
3

Echa un vistazo a esta directiva: https://github.com/samu/angular-table

Automatiza mucho la clasificación y la paginación y le brinda la libertad suficiente para personalizar su tabla / lista como lo desee.

Samuel Müller
fuente
A primera vista, parecía exactamente lo que necesitaba, pero parece que no puedo hacerlo funcionar con $ resource. Parece que siempre piensa que mi lista está vacía aquí: github.com/ssmm/angular-table/blob/master/coffee/… ... aún no he podido entender por qué. : /
Mike Desjardins
¿Cómo muestro Sno en la primera columna ya que no puedo usar $indexy mi matriz no contiene valores incrementales
Dwigh
hice esto: <td at-sortable at-attribute="index">{{sortedAndPaginatedList.indexOf(item) + 1}}</td>pero no sé si es la forma correcta
Dwigh
2

Aquí hay un código de demostración donde hay paginación + Filtrado con AngularJS:

https://codepen.io/lamjaguar/pen/yOrVym

JS:

var app=angular.module('myApp', []);

// alternate - https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination
// alternate - http://fdietz.github.io/recipes-with-angular-js/common-user-interface-patterns/paginating-through-client-side-data.html

app.controller('MyCtrl', ['$scope', '$filter', function ($scope, $filter) {
    $scope.currentPage = 0;
    $scope.pageSize = 10;
    $scope.data = [];
    $scope.q = '';

    $scope.getData = function () {
      // needed for the pagination calc
      // https://docs.angularjs.org/api/ng/filter/filter
      return $filter('filter')($scope.data, $scope.q)
     /* 
       // manual filter
       // if u used this, remove the filter from html, remove above line and replace data with getData()

        var arr = [];
        if($scope.q == '') {
            arr = $scope.data;
        } else {
            for(var ea in $scope.data) {
                if($scope.data[ea].indexOf($scope.q) > -1) {
                    arr.push( $scope.data[ea] );
                }
            }
        }
        return arr;
       */
    }

    $scope.numberOfPages=function(){
        return Math.ceil($scope.getData().length/$scope.pageSize);                
    }

    for (var i=0; i<65; i++) {
        $scope.data.push("Item "+i);
    }
  // A watch to bring us back to the 
  // first pagination after each 
  // filtering
$scope.$watch('q', function(newValue,oldValue){             if(oldValue!=newValue){
      $scope.currentPage = 0;
  }
},true);
}]);

//We already have a limitTo filter built-in to angular,
//let's make a startFrom filter
app.filter('startFrom', function() {
    return function(input, start) {
        start = +start; //parse to int
        return input.slice(start);
    }
});

HTML:

<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>
Brahim LAMJAGUAR
fuente