0

I know how to hook up ajax paging to a grid or a webgrid in asp.net mvc. But how can I accomplish ajax paging, using custom paging for large data sets for another format outside of a table grid.

Is that even possible using an mvc helper or mvc.pagedlist?

I used to be a webforms guys and it was so easy to hook up a listview where you could use divs to create whatever layout you want for individual items, you could then hook up a datapage and wrap it all in an update panel.

Basically I want a list of items that I can page through via ajax but with having large data sets I can just pull down all the items and page via jquery, I need to do custom paging on the server side and only return the items for a specific page.

Rob Carroll
  • 377
  • 1
  • 7
  • 16
  • Can we get some more details on what you are trying to accomplish? – Shai Cohen Oct 25 '12 at 20:28
  • Shai, made some updates let me know if that helps. – Rob Carroll Oct 25 '12 at 20:36
  • Have you looked at any of the closely related questions here on StackOverflow? http://stackoverflow.com/q/1403243/120955 http://stackoverflow.com/q/286022/120955 http://stackoverflow.com/q/11531456/120955 If so, how is your question different? – StriplingWarrior Oct 25 '12 at 20:50
  • 1
    I'm not looking for a grid with paging. I'm looking for a custom layout for each item in a list. This could be a series of floating divs or other layout that is not table based. – Rob Carroll Oct 25 '12 at 20:56
  • Just to make sure I understand, you want to display n items, have a link that gets n more items via ajax and appends the new items to the list of existing items? – Shai Cohen Oct 25 '12 at 21:27
  • No lets say I have 1000 items and I want to show them broken down in 25 items per page. How can I page through them using ajax. – Rob Carroll Oct 25 '12 at 21:39

3 Answers3

2

By reusing a partial view and some ajax, this is very easily done in MVC.

Add this model as a property to your page's ViewModel to handle the pagination:

namespace Models.ViewModels
{    
    [Serializable()]
    public class PagingInfoViewModel
    {

        public int TotalItems { get; set; }
        public int ResultsPerPage { get; set; }
        public int CurrentPage { get; set; }
        public int TotalPages {
            get { return Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(this.TotalItems) / this.ResultsPerPage)); }
        }
        public string LinkTextShowMore { get; set; }
        public string LinkTextShowingAll { get; set; }
        /// <summary>
        /// Paging url used by the jQuery Ajax function
        /// </summary>
        public string UrlGetMore { get; set; }

        public PagingInfoViewModel(string linkTextShowMore, string linkTextShowingAll, int resultsPerPage)
        {
            this.LinkTextShowMore = linkTextShowMore;
            this.LinkTextShowingAll = linkTextShowingAll;
            this.ResultsPerPage = resultsPerPage;
        }
    }
}

Add the following code to your partial view to handle the pagination:

    //Start Pagination
    //determine the value for the X for "Showing X of Y"
    {
        int currentTotal = 0;
        if ((Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage) < Model.PagingInfo.TotalItems) {
            //the current max item we are displaying is less than the total number of policies
            //display the current max item index\
            currentTotal = Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage;
        } else {
            //the current is greater than the total number of policies
            //display the total number of policies
            currentTotal = Model.PagingInfo.TotalItems;
        }
        if (Model.PagingInfo.TotalPages == 0 || Model.PagingInfo.CurrentPage == Model.PagingInfo.TotalPages) 
{
        @<li>
            <h3>@Model.PagingInfo.LinkTextShowingAll</h3>
            <p><strong>Showing @currentTotal Of @Model.PagingInfo.TotalItems</strong></p>
        </li>

        } else {
        @<li id="GetMore">
                <a href="#" id="lnkGetMore">
                    <h3>@Model.PagingInfo.LinkTextShowMore</h3>
                    <p><strong>Showing @(currentTotal) Of @Model.PagingInfo.TotalItems</strong></p>
                </a> 
        </li>
        @<script type="text/javascript"  lang="javascript">
             $('#lnkGetMore').click(function () {
                 $.ajax({
                     url: "@Model.PagingInfo.UrlGetMore",
                     success: function (data) {
                         $('#ProducerList li:last').remove();
                         $('#ProducerList').append(data);
                         $('#ProducerList').listview('refresh');
                     }
                 });
                 return false;
             });
        </script>
        }
    }

Now, the javascript at the end is specifically for a UI that uses ul's and li's, but can easily be customized for your needs.

The UrlGetMore property is set on the back end when the model is passed to the view. I am sure there is a more elegant way of doing this. Here is the code I used:

//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore == Url.RouteUrl("RouteItemList", new { page = view.PagingInfo.CurrentPage + 1 })

And finally, here is the action that handles both the initial View and the subsequent Partial View (ajax call)

public ActionResult List(UserModel user, ViewModel view, int page = 1)
{

    IQueryable<model> models = this.RetrieveModels(user, view);

    if ((models != null) && models.Count > 0) {
        view.PagingInfo.CurrentPage = page;
        view.PagingInfo.ResultsPerPage = user.Preferences.ResultsPerPage;
        view.PagingInfo.TotalItems = models.Count;

        view.items = models.Skip((page - 1) * user.Preferences.ResultsPerPage).Take(user.Preferences.ResultsPerPage).ToList();

        //build paging url used by the jQuery Ajax function
        view.PagingInfo.UrlGetMore = Url.RouteUrl("RouteList", new { page = view.PagingInfo.CurrentPage + 1 });
    }


    if (page == 1) {
        return View(view);
    } else {
        return PartialView("ListPartial", view);
    }

}

HTH.

Shai Cohen
  • 6,074
  • 4
  • 31
  • 54
2

You could create simple HtmlHelper simillar to this:

public static class HtmlPaginHelper
{        
    public static MvcHtmlString PagerNoLastPage(this AjaxHelper ajaxHelper,
                                                int page,
                                                int pageSize,
                                                bool isLastPage,
                                                Func<int, string> pageUrl,
                                                Func<int, AjaxOptions> pageAjaxOptions)
    {
        var result = new StringBuilder();

        var firstPageAnchor = new TagBuilder("a");
        firstPageAnchor.SetInnerText("<<");

        var prevPageAnchor = new TagBuilder("a");
        prevPageAnchor.SetInnerText("<");

        var nextPageAnchor = new TagBuilder("a");
        nextPageAnchor.SetInnerText(">");

        var currentPageText = new TagBuilder("span");
        currentPageText.SetInnerText(string.Format("Page: {0}", page));

        if (page > 1)
        {
            firstPageAnchor.MergeAttribute("href", pageUrl(1));
            firstPageAnchor.MergeAttributes(pageAjaxOptions(1).ToUnobtrusiveHtmlAttributes());

            prevPageAnchor.MergeAttribute("href", pageUrl(page - 1));
            prevPageAnchor.MergeAttributes(pageAjaxOptions(page - 1).ToUnobtrusiveHtmlAttributes());
        }
        if (!isLastPage)
        {
            nextPageAnchor.MergeAttribute("href", pageUrl(page + 1));
            nextPageAnchor.MergeAttributes(pageAjaxOptions(page + 1).ToUnobtrusiveHtmlAttributes());
        }

        result.Append(firstPageAnchor);
        result.Append(prevPageAnchor);
        result.Append(currentPageText);
        result.Append(nextPageAnchor);

        return MvcHtmlString.Create(result.ToString());
    }
}

... and then use it in your Razor view:

grid results go here...

@Ajax.PagerNoLastPage(Model.Query.Page,
                            Model.Query.PageSize,
                            Model.Data.IsLastPage,
                            i => Url.Action("Index", RouteValues(i)),
                            i => new AjaxOptions
                                     {
                                         UpdateTargetId = "content",
                                         InsertionMode = InsertionMode.Replace,
                                         HttpMethod = "GET",
                                         Url = Url.Action("Grid", RouteValues(i))
                                     })

where RouteValues(i) is defined for example like this:

@functions {
    private object PageRouteValues(int i)
    {
        return new
                   {
                       payId = Model.Query.PayId,
                       clientCode = Model.Query.ClientCode,
                       fromDate = Model.Query.FromDate,
                       tillDate = Model.Query.TillDate,
                       payNum = Model.Query.PayId,
                       checkNum = Model.Query.CheckNum,
                       payType = Model.Query.PayType,
                       payStatus = Model.Query.PayStatus,
                       page = i,
                       pageSize = Model.Query.PageSize
                   };
    }
}
Andrey Ilnitsky
  • 456
  • 4
  • 4
1

Is that even possible using an mvc helper or mvc.pagedlist?

Yes, but of course you have to coordinate the client-side requests with server-side actions to handle the actual data paging. In that sense, it's not as simple as as WebForms, but it's still possible.

Here's an example of using PagedList to render each returned item in its own table, separated by horizontal rules. You should easily be able to modify the HTML in the example to produce any rendering you want.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315