1

I have a list of objects which uses paging in my home > index.cshtml. When a user clicks through each page of objects I only want to refresh that portion of the page and nothing else. How would I accomplish this?

Ideally I want to use Async Methods and ControllerActions...

Controllers > HomeController.cs

public ViewResult Index(int page = 1) {
ProductsListViewModel viewModel = new ProductsListViewModel {
Products = repository.Products
.OrderBy(p => p.ProductID)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo {
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = repository.Products.Count()
}
};
return View(viewModel);
}

Home > Index.cshtml:

@model SportsStore.WebUI.Models.ProductsListViewModel
@{
ViewBag.Title = "Products";
}
@foreach (var p in Model.Products) {
<div class="item">
<h3>@p.Name</h3>
@p.Description
<h4>@p.Price.ToString("c")</h4>
</div>
}
<div class="pager">
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x}))
</div>

HtmlHelper > PagingHelper.cs

namespace SportsStore.WebUI.HtmlHelpers {
public static class PagingHelpers {
public static MvcHtmlString PageLinks(this HtmlHelper html,
PagingInfo pagingInfo,
Func<int, string> pageUrl) {
StringBuilder result = new StringBuilder();
for (int i = 1; i <= pagingInfo.TotalPages; i++) {
TagBuilder tag = new TagBuilder("a"); // Construct an <a> tag
tag.MergeAttribute("href", pageUrl(i));
tag.InnerHtml = i.ToString();
if (i == pagingInfo.CurrentPage)
tag.AddCssClass("selected");
result.Append(tag.ToString());
}
return MvcHtmlString.Create(result.ToString());
}
}
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
J0NNY ZER0
  • 707
  • 2
  • 13
  • 32

1 Answers1

3

You could use AJAX. The first step is to put the contents that you want to be refreshed into a partial and into its own div:

@model SportsStore.WebUI.Models.ProductsListViewModel
@{
    ViewBag.Title = "Products";
}

<div id="products">
    @Html.Partial("_products", Model.Products)
</div>

<div class="pager">
    @Html.PageLinks(Model.PagingInfo, x => Url.Action("Index", new { page = x }))
</div>

and then of course you will have the corresponding _Products.cshtml partial:

@model IEnumerable<SportsStore.WebUI.Models.ProductViewModel>
@foreach (var p in Model.Products) {
    <div class="item">
        <h3>@p.Name</h3>
        @p.Description
        <h4>@p.Price.ToString("c")</h4>
    </div>
}

and then adapt your controller action so that it is capable of responding to AJAX requests:

public ActionResult Index(int page = 1) 
{
    var viewModel = new ProductsListViewModel 
    {
        Products = repository.Products
            .OrderBy(p => p.ProductID)
            .Skip((page - 1) * PageSize)
            .Take(PageSize),
        PagingInfo = new PagingInfo 
        {
            CurrentPage = page,
            ItemsPerPage = PageSize,
            TotalItems = repository.Products.Count()
        }
    };
    if (Request.IsAjaxRequest())
    {
        return PartialView("_Products", viewModel);
    }

    return View(viewModel);
}

and now all that's left is to AJAXify your pagination anchors. Could be done in a separate javascript file where you could use jQuery to subscribe to the .click() event of them and replace the default action with an AJAX request:

$(function() {
     $('.pager a').click(function() {
         $.ajax({
             url: this.href,
             type: 'GET',
             cache: false,
             success: function(products) {
                 $('#products').html(products);
             }
         });

         // prevent the default redirect
         return false;
     });
});
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • By changing the return type of your `Index` action from `ViewResult` to `ActionResult`. I have updated my answer. I haven't noticed that you have modified this. You should always leave `ActionResult `as return type to your actions which is the highest class in the hierarchy of results. – Darin Dimitrov Jun 27 '12 at 06:58
  • No, `return PartialView("_Products", viewModel);` should not include the master layout. Are you sure that the AJAX request worked and that you entered the `if` condition? – Darin Dimitrov Jun 27 '12 at 07:06
  • You are getting a 500 error. Is there an exception thrown in your controller action? Or while rendering the partial? Use FireBug to explore the Response of the AJAX request and see the exact reason for this error. The fact that you have 2 pagers shouldn't change or complicate anything. It should work. – Darin Dimitrov Jun 27 '12 at 07:23
  • That's because of the `cache: false` AJAX parameter which adds a random number to ensure that the request is not cached by the browser. You could use POST instead of GET in which case you no longer need `cache: false`. But this shouldn't be the cause for your problem. – Darin Dimitrov Jun 27 '12 at 07:26
  • The link already points to the correct controller action with the page query string parameter. All that the javascript does is to AJAXify this link. So make sure that your links point to the correct action. – Darin Dimitrov Jun 27 '12 at 07:28
  • Of course that it matters. I can't see you loading `jQuery` anywhere which might explain why your custom js doesn't work as it depends on jquery :-) You should really learn to use FireBug as those sort of errors are already written to you on the console and you don't need to ask every time here. And by the way you can get rid of `MicrosoftAjax.js`. That's completely obsolete and no longer used in ASP.NET MVC 3. – Darin Dimitrov Jun 27 '12 at 07:39
  • Alright, that's good. So what do you see in FireBug? What error are you getting? – Darin Dimitrov Jun 27 '12 at 07:43
  • And what does the error message say? Try to narrow down the problem by excluding all unnecessary scripts and code and leaving only the strict minimum. – Darin Dimitrov Jun 27 '12 at 08:34
  • Yeah, and what's the problem? Is the corresponding controller action invoked? Normally this should be the Index controller action. lso I can see that in your code the Index controller action takes a parameter called `page` whereas in the url there's `movepage`. Could this be the problem? I told you that you could remove this random parameter by removing the `cache: false` option from your AJAX request. But this **SHOULDN'T BE THE ISSUE**. It is just a random query string parameter. It won't change anything if you remove it. But you may try. Be careful because now you might get cached versions. – Darin Dimitrov Jun 27 '12 at 08:53
  • To prevent this from happening use the POST verb in the AJAX request instead of GET. – Darin Dimitrov Jun 27 '12 at 08:57
  • Alright, I don't know what to say more. You are talking about a 500 error message but unfortunately I cannot read your mind. You will have to debug your code. – Darin Dimitrov Jun 27 '12 at 08:57
  • What do you wanna chat about? I don't have much time chattering around. Do you have specific questions/issues/code allowing to reproduce them? – Darin Dimitrov Jun 27 '12 at 17:44
  • If you send me your project I will be happy to take a look at it and help you solve the problem. – Darin Dimitrov Jun 27 '12 at 18:14
  • Got it. Will take a look after the Euro 2012 semi-final Spain vs Portugal and respond to you quickly. – Darin Dimitrov Jun 27 '12 at 18:27
  • Here's a sample MVC application I created that illustrates the concept that you are trying to implement and that you could use as a starting point: http://www.sendspace.com/file/f3vbkb – Darin Dimitrov Jun 27 '12 at 20:50
  • Thanks so much! You really cleaned it up. ( : – J0NNY ZER0 Jun 27 '12 at 20:59
  • Not to get off topic, but I have integrated SqlMembership into my custom database and in my custom tables I have a UserId which maps to the GUID AspNet_UserId column in AspNet_User Table. A mover can create a move, as you saw in the app and it is associated with him. My issue is I have Role based security but I trying to figure out how to set security so only only Mover with UserId can access edit view that has model data containing his UserId...I looked at this post but it seems incomplete/confusing... http://stackoverflow.com/questions/4237394/asp-net-mvc-3-using-authentication – J0NNY ZER0 Jun 27 '12 at 21:07
  • Your solution worked like a charm and it's so clean. I just had to make some slight mod's to it because I am using EF Repositories. Thumbs up man. I hope your team won the EURO! – J0NNY ZER0 Jun 27 '12 at 23:16
  • @DarinDimitrov This is an older question but what would this use Ajax.BeginForm() or Html.BeginForm()? – Doug Chamberlain Aug 09 '13 at 13:37