Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
На многих сайтах можно встретить такую функциональность как inifinite scrolling или бесконечный кроллинг, который предполагает постепенную подгрузку контента по мере прокрутки пользователем браузера. Стандартный пример: прокрутка тем в вконтакте, когда происходит плавная подгрузка постов. Попробуем реализовать подобную возможность.
Определим модель данных, которые будут подгружаться:
public class Phone { public int Id { get; set; } public string Name { get; set; } public string Company { get; set; } }
Контроллер будет выглядеть следующим образом:
public class HomeController : Controller { List<Phone> phones; const int pageSize = 8; public HomeController() { phones = new List<Phone> { new Phone {Id=1, Name="Samsung Galaxy III", Company="Samsung"}, new Phone {Id=2, Name="Samsung Galaxy IV", Company="Samsung"}, new Phone {Id=3, Name="Samsung Galaxy Ace II", Company="Samsung"}, new Phone {Id=4, Name="iPhone 5s", Company="Apple"}, //................................. new Phone {Id=25, Name="LG G3", Company="LG"} }; } public ActionResult Index(int? id) { int page = id ?? 0; if (Request.IsAjaxRequest()) { return PartialView("_Items", GetItemsPage(page)); } return View(GetItemsPage(page)); } private List<Phone> GetItemsPage(int page=1) { var itemsToSkip = page * pageSize; return phones.OrderBy(t=>t.Id).Skip(itemsToSkip). Take(pageSize).ToList(); } }
Переменная pageSize
указывает, сколько элементов будет на странице. Метод Index получает номер страницы, и по нему отдает нужные данные.
При первом обращении к сайту метод Index будет возвращать представление, в которое будет передаваться результат функции GetItemsPage()
Функции GetItemsPage()
по сути выполняет пагинацию данных, выбирая ту часть, которая соответствует номеру странице и количеству элементов на странице.
Если же запрос представляет Ajax-запрос, то метод Index возвращает частичное представление _Items, в которое опять де передается результат функции GetItemsPage()
.
Создадим частичное представление "_Items.cshtml":
@model IEnumerable<InfiniteScrollApp.Models.Phone> @foreach (var item in Model) { <div> <h3>@item.Name</h3> <p>@item.Company</p> </div> }
Оно выводит список переданных в него объектов.
И создадим главное представление Index.cshtml:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>@ViewBag.Title - My ASP.NET Application</title> </head> <body> <div id="scrolList"> @Html.Partial("_Items") </div> <div id="loading"><img src='@Url.Content("~/Content/loading_spinner.gif")'></div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") <script type="text/javascript"> $(function () { $('div#loading').hide(); var page = 0; var _inCallback = false; function loadItems() { if (page > -1 && !_inCallback) { _inCallback = true; page++; $('div#loading').show(); $.ajax({ type: 'GET', url: '/Home/Index/' + page, success: function (data, textstatus) { if (data != '') { $("#scrolList").append(data); } else { page = -1; } _inCallback = false; $("div#loading").hide(); } }); } } // обработка события скроллинга $(window).scroll(function () { if ($(window).scrollTop() == $(document).height() - $(window).height()) { loadItems(); } }); }) </script> </body> </html>
Для загрузки данных в представлении определен блок с id="scrolList". Он по умолчанию содержит вызвов частичного представления _Items.cshtml: @Html.Partial("_Items")
,
благодаря чему уже при первом обращении у нас будет некоторое начальное количество элементов.
И также здесь используется специальный блок с id="loading" для отображения индикатора загрузки с помощью gif-анимации:
Всю основную работу делает javascript. Чтобы отследить прокрутку, применяем обработчик $(window).scroll()
, который вызывает функцию
loadItems()
В функции loadItems()
с помощью ajax-запроса происходит подгрузка дополнительных данных.
В Web Api все будет аналогично, за исключением отправки данных. Допустим, у нас есть следующий контроллер Web API:
public class ItemsController : ApiController { List<Phone> phones; const int pageSize = 8; public ItemsController() { phones = new List<Phone> { new Phone {Id=1, Name="Samsung Galaxy III", Company="Samsung"}, new Phone {Id=2, Name="Samsung Galaxy IV", Company="Samsung"}, //.............................................. new Phone {Id=21, Name="LG G3", Company="LG"} }; } [Route("api/items/get/{page}")] public IHttpActionResult GetItems(int page) { var itemsToSkip = page * pageSize; return Ok(phones.OrderBy(t=>t.Id).Skip(itemsToSkip).Take(pageSize).ToList()); } }
И загрузка данных на html-странице:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Прокрутка в Web API</title> </head> <body> <div id="scrolList"></div> <div id="loading"><img src='@Url.Content("~/Content/loading_spinner.gif")'></div> @Scripts.Render("~/bundles/jquery") <script type="text/javascript"> $(function () { $('div#loading').hide(); var page = 0; var _inCallback = false; loadItems(); function loadItems() { if (page>-1 && !_inCallback) { _inCallback = true; $('div#loading').show(); $.ajax({ type: 'GET', url: '/api/items/get/'+page, success: function (data, textstatus) { if (data.length > 0) { var htmlLine = ""; $.each(data, function (index, item) { htmlLine += "<div><h3>" + item.Name + "</h3><p>" + item.Company + "</p></div>"; }) $("#scrolList").append(htmlLine); page++; } else { page = -1; } _inCallback = false; $("div#loading").hide(); } }); } } $(window).scroll(function () { if ($(window).scrollTop() == $(document).height() - $(window).height()) { loadItems(); } }); }) </script> </body> </html>
В представлении все то е самое, только данные получаем не в виде частичного представления, а в виде объектов javascript.