Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET MVC Ajax Infinite Scroll

0.00/5 (No votes)
3 Nov 2013 1  
This is a simple infinite scroll with Ajax post and JSON. Have fun :)

Introduction

Infinite Scroll is a web design technique that prevents the browser scroll bar from scrolling to the bottom of the page, causing the page to grow with additional content instead.

Here, I have implemented Infinite Scroll with ASP.NET MVC, Ajax post and JSON result. With rendering Partial View to string, it makes it easy to use Ajax post and JSON result to display any complex view.

Using the Code

You can also find the code on Codeplex.

Getting Data

In this sample for data, we use some XML file with book items.

Here is some part of the XML file:

<book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications with XML.</description>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, an evil sorceress, 
      and he own childhood to become queen of the world.</description>
   </book>   

We use Linq to XML to load data to our Book class, which is:

    public class Book
    {
        public string ID { get; set; }
        public string Author { get; set; }
        public string Title { get; set; }
        public string Genre { get; set; }
        public string Price { get; set; }
        public string PublishDate { get; set; }
        public string Description { get; set; }
    } 

For example, at first the user can see 5 books on the page, and when she/he scrolls down, the next 5 books will load and so on.

Number 5 is BlockSize, and BlockNumber is 1 at first, and growing by 1 every time, we scroll down and next block loads. So GetBooks function gives the BlockNumber 'th block with BlockSize books in it.

    public static class DataManager
    {
        /// <summary>
        /// Returns one block of book items
        /// </summary>
        /// <param name="BlockNumber">Starting from 1</param>
        /// <param name="BlockSize">Items count in a block</param>
        /// <returns></returns>
        public static List<Book> GetBooks(int BlockNumber, int BlockSize)
        {
            int startIndex = (BlockNumber - 1) * BlockSize;

            var filePath = AppDomain.CurrentDomain.BaseDirectory + "testData.xml";
            var doc = XDocument.Load(filePath);

            var books = (from p in doc.Descendants("book")
                        select new Book
                        {
                            ID = p.Attributes("id").Single().Value,
                            Author = p.Element("author").Value,
                            Title = p.Element("title").Value,
                            Genre = p.Element("genre").Value,
                            Price = p.Element("price").Value,
                            PublishDate = p.Element("publish_date").Value,
                            Description = p.Element("description").Value,
                        }).Skip(startIndex).Take(BlockSize).ToList();

            return books;
        }
    }  

This part is very similar to paging task.

Controllers and Views

To show one block of books, we have an Action with PartialView BookList:

        [ChildActionOnly]
        public ActionResult BookList(List<Book> Model)
        {
            return PartialView(Model);
        }   
@model List<mvcajaxinfinitescroll.Models.Book>
@foreach (var item in Model)
{
    <div style="height: 300px; background-color: #F3F3F3; margin-bottom: 30px;">
        <div style="padding: 30px; 
        font-size: 25px; color: #7b4f9d">@item.Title</div>
        <div style="padding-left: 30px; 
        font-size: 18px;">Author: @item.Author</div>
        <div style="padding-left: 30px; 
        font-size: 18px;">Genre: @item.Genre</div>
        <div style="padding-left: 30px; 
        font-size: 18px;">Price: @item.Price $</div>
        <div style="padding-left: 30px; 
        font-size: 18px;">Publish Date: @item.PublishDate</div>
        <div style="padding-left: 30px; padding-right: 30px; font-size: 18px;">
        Description: @item.Description</div>
    </div>
}   

On Index Action, we just get the first Block:

        public ActionResult Index()
        {
            int BlockSize = 5;
            var books = DataManager.GetBooks(1, BlockSize);
            return View(books);
        }  

and show it on view:

<div id="bookListDiv">
    @{Html.RenderAction("BookList", "Home", new { Model = Model });}
</div>  

We have InfinateScroll Post Action. It runs every time we scroll page down and need to populate the next block of data.

        [HttpPost]
        public ActionResult InfinateScroll(int BlockNumber)
        {
            //////////////// THis line of code only for demo. Needs to be removed ////
            System.Threading.Thread.Sleep(3000);
            //////////////////////////////////////////////////////////////////////////
            int BlockSize = 5;
            var books = DataManager.GetBooks(BlockNumber, BlockSize);
            JsonModel jsonModel = new JsonModel();
            jsonModel.NoMoreData = books.Count < BlockSize;
            jsonModel.HTMLString = RenderPartialViewToString("BookList", books);
            return Json(jsonModel);
        } 

This Action returns a Json. We wrote JsonModel class to return here as JsonResult:

public class JsonModel
    {
        public string HTMLString { get; set; }
        public bool NoMoreData { get; set; }
    } 

NoMoreData indicates that this is the last set of data and BlockNumber is the last number.

HTMLString is the HtmlString of BookList view. In this way, it is easy to return JSON and show next block using a little jquery. To get HtmlString, we have function RenderPartialViewToString.

protected string RenderPartialViewToString(string viewName, object model)
        {
            if (string.IsNullOrEmpty(viewName))
                viewName = ControllerContext.RouteData.GetRequiredString("action");

            ViewData.Model = model;

            using (StringWriter sw = new StringWriter())
            {
                ViewEngineResult viewResult = 
                ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
                ViewContext viewContext = new ViewContext
                (ControllerContext, viewResult.View, ViewData, TempData, sw);
                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }

And now let's see JavaScript we use to do Ajax post. On Index view, we have:

 <script type="text/javascript">
    var BlockNumber = 2;  //Infinate Scroll starts from second block
    var NoMoreData = false;
    var inProgress = false;

    $(window).scroll(function () {
        if ($(window).scrollTop() == $(document).height() - 
        $(window).height() && !NoMoreData && !inProgress) {

            inProgress = true;
            $("#loadingDiv").show();
            
            $.post("@Url.Action("InfinateScroll", 
            "Home")", { "BlockNumber": BlockNumber },
                    function (data) {
                        
                        BlockNumber = BlockNumber + 1;
                        NoMoreData = data.NoMoreData;
                        $("#bookListDiv").append(data.HTMLString);
                        $("#loadingDiv").hide();
                        inProgress = false;
                    });
        }
    });
</script> 
<div id="loadingDiv" 
style="text-align: center; display: none; margin-bottom: 20px;">
    <img alt="Loading" 
    src="@Url.Content("~/Content/Images/ajax-loader.gif")" />
</div>  

Please, let me know if you liked it :) ...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here