Supporting pagination in your URLs

This recipe is sort of a mix of routing and tomfoolery. While the routing aspect of this is pretty simple, the underlying bits that populate the URL can be a bit more complex. For that reason, we will cover both the routing aspect and a reusable control for managing pagination in any data set.

How to do it...

  1. Create a new MVC application.
  2. Add a reference to NBuilder (in the dependencies folder), so that we can create some data to play with.
  3. Then open up your Global.asax, so that we can add a new route to handle paging. We will create a route that will allow us to use our HomeController without the customer knowing it, and that will expose a page variable instead of the id variable. Also, instead of making the page variable optional, we will set the default value to page 1.

    Global.asax:

    routes.MapRoute( "Products", // Route name "ProductCatalog/Products/{page}", // URL with parameters new { controller = "Home", action = "Products", page = 1 } // Parameter defaults
    );
    
  4. Now that we have a route that accepts page numbers, we need to create a Products action to catch the page number in the HomeController.

    Controllers/HomeController.cs:

    public ActionResult Products(int page)
    {
    return View();
    }
    
  5. Before we get into the details of our new action, we need to discuss the model that we are going to create and use. As you may have guessed, we are going to build a products catalog, inside of which we will display products. For that reason we need to create a Product class.

    Models/Products.cs:

    public class Product
    {
    public string ProductName { get; set; }
    public double Price { get; set; }
    public string Description { get; set; }
    }
    
  6. Next, we need to create a couple of data transfer objects or view models, depending on the school of thought you subscribe too. The first one will be the Pager, which we will use to keep track of our pagination data such as what page we are on, how many pages we have in the set of data we are working with, whether there is a next or previous page, and so on. This is a simple class with a collection of properties. The constructor is used to set the data that this class will require.

    Models/Pager.cs:

    public class Pager
    {
    public Pager (int currentPage, int numberOfPages, string action, string controller)
    {
    CurrentPage = currentPage;
    NumberOfPages = numberOfPages;
    Action = action;
    Controller = controller;
    }
    public bool ShowPrevious {
    get
    {
    if (CurrentPage == 1)
    return false;
    else
    return true;
    }
    }
    public bool ShowNext {
    get
    {
    if (CurrentPage < (NumberOfPages-1))
    return true;
    else
    return false;
    }
    }
    public int PreviousPage
    {
    get
    {
    if (CurrentPage - 1 > 0)
    return CurrentPage - 1;
    else
    return 1;
    }
    }
    public int NextPage
    {
    get
    {
    if (CurrentPage + 1 <= NumberOfPages)
    return CurrentPage + 1;
    else
    return NumberOfPages;
    }
    }
    public int CurrentPage { get; set; }
    public int NumberOfPages { get; set; }
    public string Controller { get; set; }
    public string Action { get; set; }
    }
    
  7. Next, we need to create a ProductsModel class, which we will use to transfer our collection of product data as well as the Pager instance. We will use these two bits of data to build the list of products data and to control how our paging control displays.

    Models/ProductsModel.cs:

    public class ProductsModel
    {
    public List<Product> Products { get; set; }
    public Pager Pager { get; set; }
    }
    
  8. Now that we have our Product, Pager, and ProductsModel created, we can move on to the implementation of our Products action. In this action, we will create a list of products using NBuilder. Then we will set the properties of our Pager class (current page, total number of pages, the action to process the next page, and the controller that the action lives in). We will then take the list of products and our configured pager and stick them into our ProductsModel. With the ProductsModel created and hydrated with our data, we will then pass that down to our view.

    Controllers/HomeController.cs:

    public ActionResult Products(int page)
    {
    ProductsModel pm = new ProductsModel();
    List<Product> products = Builder<Product>
    .CreateListOfSize(pageCount * pageCount)
    .Build().ToList();
    Pager pager = new Pager(page, (products.Count/pageCount), "Products", "Home");
    pm.Products = products.Skip(page * pageCount) .Take(pageCount).ToList();
    pm.Pager = pager;
    return View(pm);
    }
    
  9. Now we can create our view. Right-click on the action and add a view. Set this view to be a list of Product. When the view opens up, you will need to change the type that the page inherits from IEnumerable<Product> to ProductsModel. Then update the foreach statement to iterate over a collection of Model.Products. (I removed some of the fluff that the wizard adds.)

    Views/Home/Products.aspx:

    <table>
    <tr>
    <th>
    ProductName
    </th>
    <th>
    Price
    </th>
    <th>
    Description
    </th>
    </tr>
    <% foreach (var item in Model.Products) { %>
    <tr>
    <td>
    <%: item.ProductName %>
    </td>
    <td>
    <%: String.Format("{0:C}", item.Price) %>
    </td>
    <td>
    <%: item.Description %>
    </td>
    </tr>
    <% } %>
    </table>
    
  10. Now we can create our pagination control (a partial view). Start by creating a new partial view in the Views/Shared directory called PagerView. This view will be strongly-typed, based on the Pager class that we created earlier. Then we will use the properties we created on the Pager class to show the next and previous ActionLinks. We will also perform some logic to determine what page we are on, so that we can highlight the appropriate page link. We will also determine how many pages there are in the set of data, so that we can show a link for each page. (I added some CSS classes to the output to help control the look and feel.)

    Views/Shared/PagerView.ascx:

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl< MvcApplication1.Models.Pager>" %>
    <div class="Pager">
    <% if(Model.ShowPrevious) { %>
    <div class="PagerPreviousPage">
    <%= Html.ActionLink("<< Previous",Model.Action, Model.Controller, new { page = Model.PreviousPage }, null) %></div>
    <% } %>
    <div class="PagerPageNavigation">
    <% for (int i = 1; i < Model.NumberOfPages;i++) { %>
    <%
    string className = "PagerPageNavItem";
    string activeClassName = "PagerActivePageNavItem";
    string currentClassName = "";
    if (i == Model.CurrentPage)
    currentClassName = activeClassName;
    else
    currentClassName = className;
    %>
    <div class="<%= currentClassName %>">
    <%= Html.ActionLink(i.ToString(),Model.Action, Model.Controller, new { @page = i}, null) %></div>
    <% } %>
    </div>
    <% if(Model.ShowNext) { %>
    <div class="PagerNextPage">
    <%= Html.ActionLink("Next >>",Model.Action, Model.Controller, new { @page = Model.NextPage }, null) %></div>
    <% } %>
    </div>
    
  11. Finally, we are ready to drop in our new page control. Go back to the Products.aspx view. Add a call to Html.RenderPartial above the table you created earlier. Specify the name of our new PagerView. Also, pass in the instance of the Pager class that we instantiated and hydrated earlier.

    Views/Shared/Products.aspx:

    ...
    <% Html.RenderPartial("PagerView", Model.Pager); %>
    <div style="display:block;clear:both;" />
    ...
    
  12. You are now ready to build the site. Navigate to the /ProductsCatalog/Products URL. This will use the default Page value of one and should display ten product records. You can click on Next to get the next set of data. Then you can click on Previous. You can also click on the page number you want to see, or you can edit the URL page number directly.

How it works...

The route we created in the first part of the recipe catches all of the URLs that begin with ProductCatalog. It also expects the Products action to be called and sets the initial page to one. The majority of the magic was then handled by the Pager class that we created, which manages all the paging data, such as what page we are currently viewing, how many pages there are, and so on. The other important part of the magic came in the form of our query structure—in that we controlled how many records to show and which set of data we were on.

Some of this logic could have probably been moved into the Pager class, but it seemed more appropriate, and efficient to keep it with the query for this example. You could probably create a generic Pager class that takes in a type to operate on. You could also extend the Pager a bit to take in a repository class according to an interface with a standard method to call, that takes the current page number and the number of records to show. That is a bit bigger topic than this book is prepared to cover though.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset