Zdrojový kód je dostupný zde.
Models
Aby to nebylo moc složité, bude se zobrazovat seznam jmen, tedy jako základní třída nám poslouží toto:public class UserViewModel { public string FirstName { get; set; } public string LastName { get; set; } }
Pro zobrazení seznamu bude sloužit generická třída - takže ani tento příklad nebude pevně svázán s třídou UserViewModel, ale generickou třídu budeme moci použít s jakoukoliv třídou, z níž chceme zobrazovat data.
public interface IItemListInfo { int TotalItems { get; } } public class ItemListViewModel<T> : IItemListInfo { private IEnumerable<T> items; private int totalItems; public ItemListViewModel(IEnumerable<T> items, int totalItems) { this.items = items; this.totalItems = totalItems; } public IEnumerable<T> Items { get {return this.items;} } public int TotalItems { get { return this.totalItems; } } }
a ještě musíme mít třídu, která bude držet "stránkovácí" informace:
public class PagingViewModel { public int PageNumber { get; set; } public int PageSize { get; set; } public int Pages {get;set;} }
Controller - poprvé
Akce v kontroleru bude jednoduchá - hlavním úkolem je načíst daný počet dat (=počet záznamů na stránku) pro danou stránku (počet stránek)
public ActionResult Index(PagingViewModel paging) { ItemListViewModel<UserViewModel> model = this.GetUsers(paging.PageSize, paging.PageNumber); return View(model); }
Views
Ve view dojde k zrendrování informací a i k vykreslení navigační části. První view je docela jednoduché a vykreslí informace o uživateli - pro vykreslení navigační části pak předá řízení zpět do kontroleru a zavolá akci "Paging"
@using MvcPagingExample.Models; @model ItemListViewModel<UserViewModel> @{ ViewBag.Title = "Home Page"; } <div class="list"> <h2>List of Users</h2> <table> <thead> <th width="120px">First name</th> <th width="120px">Last name</th> </thead> @foreach (UserViewModel user in Model.Items) { <tr> <td>@user.FirstName </td> <td>@user.LastName</td> </tr> } </table> <br/> @{Html.RenderAction("Paging");} </div>
Controller - podruhé
Tato metoda může být použita pro vykreslení stránkování libovolného seznamu - odpovídající view jen využívá javascript pro ajax volání zpět na server a vrácenou hodnotou nahrádí stávající obsah:[ChildActionOnly] public ActionResult Paging(PagingViewModel paging) { return PartialView(paging); }
a view:
@using MvcPagingExample.Models; @model PagingViewModel <script type="text/javascript"> $(document).ready(function () { jQuery("a").click(function () { var $anchor = jQuery(this); var url = $anchor.closest('.ajaxPaging').attr("data-source"); jQuery.get( url, { PageNumber: $anchor.attr("rel")}, function (data) { jQuery(".list").replaceWith(data); }); }); }); </script> <div class="ajaxPaging" data-source="@Request.Url.AbsolutePath"> @NaviLink(active: Model.PageNumber > 1, targetPageNumber: 1, label: "first") @NaviLink(active: Model.PageNumber > 1, targetPageNumber: (Model.PageNumber - 1), label: "previous") <b>page @Model.PageNumber of @Model.Pages</b> @NaviLink(active: Model.PageNumber < Model.Pages, targetPageNumber: (Model.PageNumber + 1), label: "next") @NaviLink(active: Model.PageNumber < Model.Pages, targetPageNumber: Model.Pages, label: "last") </div> @helper NaviLink(bool active, int targetPageNumber, string label) { if (active) { <a href="#" rel="@targetPageNumber">@label</a> } else { @label } }
Prvotní nastavení
Zbývá ještě ošetřit případ, kdy při prvním volání akce není žádné stránkování nastaveno. K tomu slouží atribut třída, která dědí z třídy ActionFilterAttribute a která nabízí dvě metody - jednu spouštěnou před provedením vlastní akce kontroleru (OnActionExecuting) a druhou, která se provadí po vykonání akce v kontroleru (OnActionExecuted).public override void OnActionExecuting(ActionExecutingContext filterContext) { var paging = filterContext.ActionParameters.Select(p => p.Value as PagingViewModel).Where(f=> f !=null).FirstOrDefault(); if (paging != null) { paging.PageSize = PageSize; filterContext.RouteData.Values.Add("PageSize", this.PageSize); if (paging.PageNumber == 0) { filterContext.RouteData.Values.Add("PageNumber", 1); paging.PageNumber = 1; } } base.OnActionExecuting(filterContext); }
Nejprve se metoda pokouší najít hodnotu parametru předávaného metodě kontroleru - tedy PagingViewModel. Pokud takový parametr nenajde, tak jej vytvoří a nastaví velikost stránky a první stránku jako aktuální. Zároveň tyto hodnoty vloží i do kolekce RouteData. Tato kolekce je MVC používáná při bindování a pokud tedy budeme později volat metodu Paging, tak MVC použije data z této kolekce pro vytvoření parametru (instance třídy PagingViewModel) předávaného do této metody.
Po spuštění metody se naplní kolekce a zároveň se zjistí, kolik záznamů je celkem k dispozici. Což je ten správný okamžik pro spuštění metody OnActionExecuted, která vypočte celkový počet stránek a získá tak poslední hodnotu, kterou je nutno uložit do kolekce RouteData, aby MVC mohlo později, při volání metody Paging, vytvořit plnohodnotnou instanci třídy PagingViewModel.
public override void OnActionExecuted(ActionExecutedContext filterContext) { var model = filterContext.Controller.ViewData.Model as IItemListInfo; if (filterContext.HttpContext.Request.IsAjaxRequest()) { ViewResult result = filterContext.Result as ViewResult; filterContext.Result = new PartialViewResult() { TempData = result.TempData, View=result.View, ViewData=result.ViewData, ViewEngineCollection=result.ViewEngineCollection}; } if (model != null) { filterContext.RouteData.Values.Add("Pages", (int) Math.Ceiling( (double) model.TotalItems/this.PageSize)); } base.OnActionExecuted(filterContext); }
Můžete si také všimnout, že je kontrolován typ příchozího volání a pokud se jedná o Ajax volání, je výsledek změněn na PartialView - takže se rendreju pouze view odpovídající danému kontroleru a master view je ignorováno - vrací se tak pouze zrendrovaná tabulka a nikoliv celá stránka.
Tento atribut je pak použit pro dekorování akce a zároveň nastavuje velikost stránky:
[Paging(PageSize = 5)] public ActionResult Index(PagingViewModel paging) { ItemListViewModel<UserViewModel> model = this.GetUsers(paging.PageSize, paging.PageNumber); return View(model); }
Může se použít na libovolnou akci, která vyžaduje pro své výsledky stránkování. Vývojář se o stránkování nemusí starat a v kontroleru se tedy může soustředit pouze na získání dat.
Žádné komentáře:
Okomentovat