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
{
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)
{
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; 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 :) ...