[ASP.NET MVC 3] Paginar información automáticamente con el scroll usando JQuery

No sé sí el título será muy acertado pero lo que quiero mostrar es como cargar más información en nuestra página tal cual lo hace Facebook, Google Reader… cuando la barra de scroll llega al final. (Al final del artículo está el código de ejemplo)

Para ello vamos a hacer uso de JQuery y del evento del scroll de la ventana. En este caso no necesitamos ninguna librería adicional de JQuery pues ya lo tenemos disponible cuando creamos un proyecto de ASP.NET MVC 3.

Lo primero es crear un proyecto vacio de ASP.NET MVC 3 y dejamos por defecto el motor de vistas Razor:

image

El siguiente paso es añadir un archivo de script al que llamaremos scrolling.js con este contenido:

/// <reference path="jquery-1.4.4.min.js" />

var page = 0;

var _inCallback = false;

 

$(window).scroll(function () {

    if ($(window).scrollTop() == $(document).height() - $(window).height()) {

        loadProducts();

    }

});

 

function loadProducts() {

    if (page > -1 && !_inCallback) {

        _inCallback = true;

        page++;

        $('div#loading').html('<p><img src="Images/loader.gif"></p>');

        $.get("/Home/Index/" + page, function (data) {

            if (data != '') {

                $("#products").append(data);

            }

            else {

                page = -1;

            }

 

            _inCallback = false;

            $('div#loading').empty();

        });

    }

}

Lo primero que voy a explicar es el código de la función scroll

if ($(window).scrollTop() == $(document).height() - $(window).height()) {

    loadProducts();

}

con la siguiente imagen:

window

Para saber cuando el scroll ha llegado al final, lo que hacemos es restar al alto del documento el alto de la ventana, y esa diferencia es el recorrido del scroll de la ventana, que en este ejemplo concreto es 300, justo la diferencia de la resta de las 2 alturas que comentamos anteriormente. ¿Se ve claro no?

La función loadProducts:

function loadProducts() {

    if (page > -1 && !_inCallback) {

        _inCallback = true;

        page++;

        $('div#loading').html('<p><img src="Images/loader.gif"></p>');

        $.get("/Home/Index/" + page, function (data) {

            if (data != '') {

                $("#products").append(data);

            }

            else {

                page = -1;

            }

 

            _inCallback = false;

            $('div#loading').empty();

        });

    }

}

Cuando el scroll llegue a su tope, se llamará a función loadProducts y aquí os puede llamar la atención la variable _inCallback, no? Esa variable global la he tenido que añadir para evitar varias llamadas que se producen y que hacen que se cargue más información de la necesaria y encima en un orden que no es el correcto. Podéis probarlo quitandola.

Por lo demás, lo que hacemos es ir autoincrementado la variable page en cada llamada para llamar al controlador pasandole el parámetro id y que nos devuelva los demás registros y los vamos añadiendo al div products. También estoy añadiendo una imagen para informar que se está cargando la información.

Vamos ahora con el código de nuestro controlador, al que hemos llamado HomeController:

public class HomeController : Controller

{

    /// <summary>

    /// Fake repository

    /// </summary>

    FakeRepository _repository = new FakeRepository();

 

    /// <summary>

    /// Indexes the specified id.

    /// </summary>

    /// <param name="id">The id.</param>

    /// <returns></returns>

    public ActionResult Index(int? id)

    {

        var page = id ?? 0;

 

        var products = _repository.GetProducts(page);

 

        if (Request.IsAjaxRequest())

            return PartialView("ProductsList", products);

 

        return View(products);

    }

}

Básicamente recibe el parámetro nullable id (La primera vez que carguemos la página será nulo) que corresponde al número de página que a su vez pasamos al repositorio falso:

/// <summary>

/// Gets the products.

/// </summary>

/// <param name="page">The page.</param>

/// <returns></returns>

public IEnumerable<Product> GetProducts(int page = 1)

{

    var skipRecords = page * RecordsPerPage;

 

    return _products.

        Skip(skipRecords).

        Take(RecordsPerPage).AsEnumerable();

}

Que lo que hace es paginar los resultados segun el número de registros que queremos por página, que lo indica la constante RecordsPerPage:

private const int RecordsPerPage = 10;

En este caso 10 registros por página, y luego comprueba si es una petición Ajax, que la primera vez no lo será:

image

y cargará los primeros 10 productos, pero cuando hagamos scroll de la página y lleguemos al tope invocaremos al action method del controlador mediante Ajax con la función get de Jquery:

image

Y para ello me he creado una vista parcial para la lista de productos:

@model IEnumerable<DemoMvcScrolling.Models.Product>

 

@foreach (var product in Model)

{

    <div class="item">

        <h3>@product.Name</h3>

        @product.Description

        <h4>@product.UnitPrice.ToString("c")</h4>

    </div>

}

Que será el html que devuelva y con la función append de JQuery se lo añadiremos al div products que tenemos en la vista principal:

@model IEnumerable<DemoMvcScrolling.Models.Product>

 

@{

    ViewBag.Title = "Index";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

 

<h2>Products</h2>

 

<div id="products">

    @Html.Partial("ProductsList", Model) 

</div>

<div id="loading"></div>

Nota: cuando descarguéis el código de ejemplo veréis que he añadido un Thread.Sleep(2000); al action method del controlador para simular que tarda un poquito en cargar los datos porque sino no da ni tiempo para ver la imagen 😉

/// <summary>

/// Indexes the specified id.

/// </summary>

/// <param name="id">The id.</param>

/// <returns></returns>

public ActionResult Index(int? id)

{

    Thread.Sleep(2000);

 

    var page = id ?? 0;

 

    var products = _repository.GetProducts(page);

 

    if (Request.IsAjaxRequest())

        return PartialView("ProductsList", products);

 

    return View(products);

}

Ahora vamos a probarlo:

Un saludo y buen puente de Semana Santa a todos!!!

3 comentarios en “[ASP.NET MVC 3] Paginar información automáticamente con el scroll usando JQuery”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *