1

Lately, I'v been struggling with an annoying situation on ASP.NET MVC. Here's the story in short,
I'm supposed to have a view that lists all of the products; now because those products are too many, I'm paging them (very innovative heh!). The page contains two paging arrows -"Next 10 products", "and previous 10 products". The view is passed a IEnumerable<Product> collection containing the list of products to be displayed. The view is also passed two integers (currentPage, totalPages) as ViewData items. Now what I need to accomplish is to check if it's the first page (ViewData["CurrentPage"] == 0) I should change the css class of the "previous 10 pages" link to disabled, so I came up with something like the following

 <a href="/Products/Page<%=Html.Encode(Convert.ToInt32(ViewData["CurrentPage"])-1)%>/" 
           class="<%=Convert.ToInt32(ViewData["CurrentPage"]) <= 1 ? "bgn disabled" : ""%>">                          
                previous 10 products                
        </a>

This worked fine, still there's a problem. Though the link is disabled, or specifically grayed, it still points to a valid URL, so I tried to actually change the href attribute of the link based on the CurrentPage variable. Here's how the code looks like (get ready to the pure ugliness):

<a href="<%=Convert.ToInt32(ViewData["CurrentPage"]) <= 0 ? 
        "javascript:void[]" : 
        "/products/Page<%=Html.Encode(Convert.ToInt32(ViewData["CurrentPage"])+1)%>/" %>" 
        class="<%=Convert.ToInt32(ViewData["CurrentPage"]) <= 0 ? 
        "bgn disabled" :
        ""%>">

    previous 10 products
    </a>

Now, my problems with this code are:

  1. The second statement doesn't work, apparently because of the nested server side scripts
  2. It's very ugly, and absolutely unreadable (imagine I'm doing this with each page that requires paging! pain in the but). :(

Any better alternatives guys?

Ahmed
  • 11,063
  • 16
  • 55
  • 67

3 Answers3

2

You can use an if statement:

<% if (Convert.ToInt32(ViewData["CurrentPage"]) <= 0) { %>
     Disabled template goes here...
<% } else { %> 
     Link template goes here...
<% } %>

By the way, if you're doing this for a set of pages, you can encapsulate it in a ViewUserControl or a ViewMasterPage.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • Well, getting red of the ternary operator really helped. It looks better now, but, isn't there any other approach to refactor this even more? – Ahmed Jul 12 '09 at 10:33
  • Beside the encapsulation in a control or master page, I suggest using a strongly typed model rather than using `ViewData` directly. Other than that, you could build your own HTML helper too, if that makes sense. – Mehrdad Afshari Jul 12 '09 at 11:43
1

Here is another solution. Add <script runat="server">:

<script runat="server">
    protected string Prev10Url {
        get {
            return Convert.ToInt32(ViewData["CurrentPage"]) <= 0
                ? "javascript:void[]"
                : "/products/Page" + Html.Encode(Convert.ToInt32(ViewData["CurrentPage"])+1);
        }
    }

    protected string Prev10Class {
        get {
            return Convert.ToInt32(ViewData["CurrentPage"]) <= 0
                ? "bgn disabled"
                : "";
        }
    }

    protected string Next10Url {
        get {
            ...
        }
    }

    protected string Next10Class {
        get {
            ...
        }
    }
</script>

And then change your markup:

<a href="<%= Prev10Url %>" class="<%= Prev10Class %>">previous 10 products</a>
<a href="<%= Next10Url %>" class="<%= Next10Class %>">next 10 products</a>
eu-ge-ne
  • 28,023
  • 6
  • 71
  • 62
0

You can try my pager HTML helper:

using System;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace System.Web.Mvc
{
   public class Pager
   {
      private ViewContext viewContext;
      private readonly int pageSize;
      private readonly int currentPage;
      private readonly int totalItemCount;
      private readonly RouteValueDictionary linkWithoutPageValuesDictionary;

      public Pager(ViewContext viewContext, int pageSize, int currentPage, int totalItemCount, RouteValueDictionary valuesDictionary)
      {
         this.viewContext = viewContext;
         this.pageSize = pageSize;
         this.currentPage = currentPage;
         this.totalItemCount = totalItemCount;
         this.linkWithoutPageValuesDictionary = valuesDictionary;
      }

      public string RenderHtml()
      {
         int pageCount = (int)Math.Ceiling(this.totalItemCount / (double)this.pageSize);
         int nrOfPagesToDisplay = 8;

         var sb = new StringBuilder();
         sb.Append("<ul class=\"pagination\">");
         // Previous
         if (this.currentPage > 1)
         {
            sb.Append(string.Format("<li class=\"prev\"><a href=\"{0}\">«</a></li>", Route(this.currentPage - 1)));
         }
         else
         {
            sb.Append("<li class=\"prev disabled\"><span>«</span></li>");
         }

         int start = 1;
         int end = pageCount;

         if (pageCount > nrOfPagesToDisplay)
         {
            int middle = (int)Math.Ceiling(nrOfPagesToDisplay / 2d) - 1;
            int below = (this.currentPage - middle);
            int above = (this.currentPage + middle);

            if (below < 4)
            {
               above = nrOfPagesToDisplay;
               below = 1;
            }
            else if (above > (pageCount - 4))
            {
               above = pageCount;
               below = (pageCount - nrOfPagesToDisplay);
            }

            start = below;
            end = above;
         }

         if (start > 3)
         {
            sb.Append(GeneratePageLink("1", 1));
            sb.Append(GeneratePageLink("2", 2));
                sb.Append("<li class=\"more\">...</li>");
         }
         for (int i = start; i <= end; i++)
         {
            if (i == this.currentPage)
            {
               sb.Append(string.Format("<li class=\"page selected\"><span>{1}</span></li>", Route(i),i));
            }
            else
            {
               sb.Append(GeneratePageLink(i.ToString(), i));
            }
         }
         if (end < (pageCount - 3))
         {
                sb.Append("<li class=\"more\">...</li>");
            sb.Append(GeneratePageLink((pageCount - 1).ToString(), pageCount - 1));
            sb.Append(GeneratePageLink(pageCount.ToString(), pageCount));
         }

         // Next
         if (this.currentPage < pageCount)
         {
            sb.Append(string.Format("<li class=\"next\"><a href=\"{0}\">»</a></li>", Route(this.currentPage + 1)));
         }
         else
         {
            sb.Append("<li class=\"next disabled\"><span>»</span></li>");
         }
         sb.Append("</ul>");
         return sb.ToString();
      }
      private string Route(int pageNumber)
      {

         var pageLinkValueDictionary = new RouteValueDictionary(this.linkWithoutPageValuesDictionary);
         pageLinkValueDictionary.Add("page", pageNumber);
         var virtualPathData = RouteTable.Routes.GetVirtualPath(this.viewContext.RequestContext, pageLinkValueDictionary);
         return virtualPathData.VirtualPath;

      }
      private string GeneratePageLink(string linkText, int pageNumber)
      {
         var pageLinkValueDictionary = new RouteValueDictionary(this.linkWithoutPageValuesDictionary);
         pageLinkValueDictionary.Add("page", pageNumber);
         var virtualPathData = RouteTable.Routes.GetVirtualPath(this.viewContext.RequestContext, pageLinkValueDictionary);

         if (virtualPathData != null)
         {

            string linkFormat = "<li class=\"page\"><a href=\"{0}\">{1}</a></li>";
            return String.Format(linkFormat, virtualPathData.VirtualPath, linkText);
         }
         else
         {
            return null;
         }
      }
   }
}

How to use:

<%= Html.Pager(10, (Request["page"].IsNotNull() ? Request["page"].ToInt() : 1), ViewData["Total"].ToInt(), new { category = Request["category"], alphabet = Request["alphabet"] })%>

And Controller implementation looks like this:

public ActionResult Index(string page, string category, string alphabet)
{
   .....
   ViewData["Total"] = model.Count();
   return View(model.ToPagedList((page.IsNotNull() ? page.ToInt() - 1 : 0), 10));
}

And finally output:

pager
(source: clip2net.com)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
omoto
  • 1,212
  • 1
  • 17
  • 24
  • Well, actually I'm trying to have only two links [previous 10, and next 10]. I'm not sure if your pager helper method can help with that. Thanks though. – Ahmed Jul 12 '09 at 09:30