How to do pagination in C# MVC?
I'm trying to do something like this,
I want retrieve 1000 records from database and keep it in server side then i want to send 10 records per each page to view when user requesting the searched page.
I have gone through some examples but i couldn't get better solution, I prefer that the way i suggested or something better solution for this. (I'm using jquery as well and not using Razor)
example which I gone through
Paginated search results with LINQ to SQL
Thanks in advance
Paging in ASP.NET MVC is a solved problem by some libraries; you can find them at Nuget.org by
http://www.nuget.org/packages?q=paging.
I want retrieve 1000 records from database and keep it in server side then i want to send 10 records per each page to view when user requesting the searched page.
Retrieving 1000 records and keep them in your application wouldn't be ideal. It would be better to retrieve 10 records in every request; as you have mentioned in your question, the solution that use Skip and Take in a linq query is perfect. However, there is no problem to page cached data.
Try This
One of the easiest method
Controller
using PagedList;
public ActionResult Index(int ? pagePos)
{
//return View(db.Items.ToList());// Change this as following
int pageNumber = (pagePos ?? 1);
return View(db.Items.ToList().ToPagedList(pageNumber, 30)); //30 is the size of records in a single page
}
Index.cshtml
#*#model IEnumerable<ProjectDB.Models.Item>*#
#*Change These Also as following*#
#model PagedList.IPagedList<ProjectDB.Models.Item>
#using PagedList.Mvc
<table class="table">
<tr>
<th>
Name
</th>
<th>
ID
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ID)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
#*Pagination Code Starts*#
<div class="pageCount">
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
</div>
#Html.PagedListPager(Model, pagePos => Url.Action("Index", new { pagePos }))
Related
I want to paging in Product list with parameter Category, i using PagedList mvc
controller sample
public ActionResult ListProduct(int id, int? pagePos)
{
var list = db.List_Product.Where(e => e.CategoryID == id);
int pageNumber = (pagePos ?? 1);
return View(list.ToList().ToPagedList(pageNumber, 2));
}
and in view ListProduct.cshtml
#*#model IEnumerable<Sales.Areas.Users.Models.List_Product>*#
#model PagedList.IPagedList<sales.areas.users.models.list_product> #*Maybe error in line here*#
#using PagedList.Mvc
<table class="table">
<tr>
<th>
Name
</th>
<th>
ID
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ID)
</td>
</tr>
}
</table>
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, pagePos => Url.Action("ListProduct", new { pagePos }))
It's not working, and get this error CS0234: The type or namespace name 'IPagedLis' does not exist in the namespace 'PagedList' (are you missing an assembly reference?) although i had added reference PagedList ago.
Without more informations about the error is hard to find the culprit. You should disable custom errors and activate debug in your web.config
First check that the variable id in ActionResult ListProduct is passed.
Then in your view change:
#model PagedList.IPagedList<sales.areas.users.models.list_product>
With:
#model PagedList.IPagedList<Sales.Areas.Users.Models.List_Product>
Make sure that all necessary namespaces come before the use of any types in that namespace
#using PagedList; //import this so we can cast our list to IPagedList (only necessary because ViewBag is dynamic)
#using PagedList.Mvc; //import this so we get our HTML Helper
#model IPagedList<Sales.Areas.Users.Models.List_Product>
I have a strongly typed view where I am trying to display a model when an id is passed to my Controller action method.
I am trying to construct a link to each of this models as well.
For the purpose before the beginning of my foreach I have an i=0, and then I am trying to increase it by 1 using i++. The problem is that the the i++ part doesn't work -- I have always zero as an id in my link. Why?
Can someone help with this?
#{
var i = 0;
foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Content)
</td>
<td>
#Html.ActionLink("Details", "Details", new { id = i })
i++;
</td>
</tr>
}
}
Razor does its best to distinguish code from markup/html/text, which isn't easy in general. And in this case, i++ is considered text.
The reason is that there are tags following the foreach { part, causing the reading mode to switch to markup/html/text. In that mode only #..., #{... or } are recognized as code, and everything else is treated as markup/html/text.
If this happens, just put #{ ... } around your statements, like this:
<td>
#Html.ActionLink("Details", "Details", new { id = i })
#{
i++;
}
</td>
I have a model, which can represent 3 categories. I want in my view, make 3 different tables for each category with relevant fields. I think for this I need to use partial view with viewmodel for each category.
So my main model is "Ad", which have 3 sub viewmodels (Realty, Auto and Service).
Here the example how I implement Realty action on my home controller:
public ActionResult Realty()
{
var ads = db.Ads.Include(a => a.Realty);
var vm = new List<RealtyViewModel>();
foreach (var ad in ads)
{
vm.Add(new RealtyViewModel
{
Title = ad.Title,
Descirpiton = ad.Descirpiton,
Type = ad.Realty.Type,
NumberOfRooms = ad.Realty.NumberOfRooms
});
}
return PartialView(vm);
}
Then my partial view, looks like this:
#model IEnumerable<OGAS.Areas.Category.ViewModels.RealtyViewModel>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th>
#Html.DisplayNameFor(model => model.Descirpiton)
</th>
<th>
#Html.DisplayNameFor(model => model.NumberOfRooms)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Type)
</td>
<td>
#Html.DisplayFor(modelItem => item.Descirpiton)
</td>
<td>
#Html.DisplayFor(modelItem => item.NumberOfRooms)
</td>
</tr>
}
</table>
Then in my Index page (without using any models), I call partial view like this:
#{Html.RenderPartial("Realty");}
But then I'm getting following error:
An exception of type 'System.NullReferenceException' occurred in App_Web_gdyh352c.dll but was not handled in user code
Could you please advise if this approach is good (calling 3 vms), if yes how to implement this?
Thanks.
Try to replace #{Html.RenderPartial("Realty");} and use #Html.Action("Realty") in this case, as you need to call back to the controller action, in order to create the model for the partial view.
See MVC Html.Partial or Html.Action for more information.
Use this, for .net core and mvc. #Html.Action has been removed from .net core
#await Html.PartialAsync("_YourPartialViewName", YourModel)
I am using PagedList.Mvc and I have added a nice way to navigate across various pages in a mvc web application. However, when I click on an "edit" or "details" tab and save changes I am sent back to the 1st page. I want to remain on the same page where the changes were made.
Here is the code I have in the controller:
// GET: Item
public ActionResult Index(int? page)
{
var items = db.Items.Include(i => i.PurchaseOrder);
return View(items.ToList().ToPagedList(page ?? 1, 3));
}
Here is the code I have in the view:
#using PagedList;
#using PagedList.Mvc;
#model IPagedList<PurchaseOrders.Models.Item>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.First().ItemDescription)
</th>
<th>
#Html.DisplayNameFor(model => model.First().Quantity)
</th>
<th>
#Html.DisplayNameFor(model => model.First().Price)
</th>
<th>
#Html.DisplayNameFor(model => model.First().DueDate)
</th>
<th>
#Html.DisplayNameFor(model => model.First().DateReceived)
</th>
<th>
#Html.DisplayNameFor(model => model.First().Comments)
</th>
<th>
#Html.DisplayNameFor(model => model.First().PurchaseOrder.PurchaseRequest_)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ItemDescription)
</td>
<td>
#Html.DisplayFor(modelItem => item.Quantity)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.DisplayFor(modelItem => item.DueDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateReceived)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comments)
</td>
<td>
#Html.DisplayFor(modelItem => item.PurchaseOrder.PurchaseRequest_)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ItemId }) |
#Html.ActionLink("Details", "Details", new { id=item.ItemId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ItemId })
</td>
</tr>
}
</table>
#Html.PagedListPager(Model, page => Url.Action("Index", new { page }))
Please help!
You could pass an additional 'page` parameter to your edit method, for example
In your Index method, add
ViewBag.CurrentPage = page; // or use a view model property
Then your link would be
#Html.ActionLink("Edit", "Edit", new { id=item.ItemId, page = ViewBag.CurrentPage})
Then your edit method
[HttpGet]
public ActionResult Edit(int ID, int page)
{
ViewBag.CurrentPage = page; // pass current page to edit view
And your edit view
#using (Html.BeginForm(new { page = ViewBag.CurrentPage })) {
And in you post method
[HttpGet]
public ActionResult Edit(EditModel model, int page)
{
.... // Save
return RedirectToAction("Index", new { page = page });
In this case the page is stored in the ViewBag which makes it ephemeral (the ViewBag is only available for the current request).
In the controller, you're telling it if you get null use 1 as your current page. so null is always retuned and you get the 1st page everytime.
You need to provide the current page Number to your views that you navigate to (Edit/Create) and then provide it back to the original page when you're done.
You can use the TempData,that works well on HTTP redirects and lives longer than viewbag or viewData.
You can also move it arround with your models when calling actions and then give it back to the index action that needs a page number.
You can use sessions too. BTW, TempData is using session behind the scenes.
UPDATED:
Code to add in your index action:
var page = TempData["page"];
Code to add in the Create or Edit Submit action
//Get the page number
var page = TempData["page"];
//Set it back to Tempdata (because Tempdata is only for redirects) otherwise it will be lost
TempData["page"]=page;
add the value of the parameter to TempData["page"] when calling back the index action again
You can also access it from Index action directly since we repopulated it:
var page = TempData["page"];
return View(items.ToList().ToPagedList(page ?? 1, 3));
I also had this problem.
I tried to put it in the URL at first but it seems a bit weird to have ?page=2 in our URL.
So I replaced it with using TempData
What you need to do is this:
Store the page in TempData when you are in your Index() action method;
public const string PAGE_QUERY_STRING_KEY = "page";
public ActionResult Index(int page = 1)
{
TempData[PAGE_QUERY_STRING_KEY] = page;
...
}
Then use TempData.Peek(), instead of TempData[], everywhere else (to retain the value of your page between requests which are related to your current Index page) --- in your Edit, Create, Details, etc. action methods:
public ActionResult Edit(...)
{
...
return RedirectToAction(nameof(Index), new { page = TempData.Peek(PAGE_QUERY_STRING_KEY) });
// do not do this because this will remove the temp data
// return RedirectToAction(nameof(Index), new { page = TempData[PAGE_QUERY_STRING_KEY])
}
... and in your views:
<!--(Edit.cshtml)-->
...
<p>
#Html.ActionLink("Back to List", "Index",
new { page = TempData.Peek(FLP.Web.Controllers.UsersAdminController.PAGE_QUERY_STRING_KEY) })
</p>
I have a controller called AuctionsController. In it I have Actions called Index() and AuctionCategoryListing():
//Used for displaying all auctions.
public ActionResult Index()
{
AuctionRepository auctionRepo = new AuctionRepository();
var auctions = auctionRepo.FindAllAuctions();
return View(auctions);
}
//Used for displaying auctions for a single category.
public ActionResult AuctionCategoryListing(string categoryName)
{
AuctionRepository auctionRepo = new AuctionRepository();
var auctions = auctionRepo.FindAllAuctions()
.Where(c => c.Subcategory.Category.Name == categoryName);
return View("Index", auctions);
}
As you can tell, they both invoke the same View (is this action called 'to invoke a view'. What is it's proper name?).
#model IEnumerable<Cumavi.Models.Auction>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
<th>
IDSubcategory
</th>
<th>
IDCity
</th>
<th>
IDPerson
</th>
<th>
Title
</th>
<th>
TextBody
</th>
<th>
ContactNumber
</th>
<th>
AskingPrice
</th>
<th>
AddressDirection
</th>
<th>
LatestUpdateDate
</th>
<th>
VisitCount
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
<td>
#item.IDSubcategory
</td>
<td>
#item.IDCity
</td>
<td>
#item.IDPerson
</td>
<td>
#item.Title
</td>
<td>
#item.TextBody
</td>
<td>
#item.ContactNumber
</td>
<td>
#String.Format("{0:F}", item.AskingPrice)
</td>
<td>
#item.AddressDirection
</td>
<td>
#String.Format("{0:g}", item.LatestUpdateDate)
</td>
<td>
#item.VisitCount
</td>
</tr>
}
</table>
They both inherit from the same Model.
My question is, am I doing things the right appropriate way? Or is this just a hack I managed to scrape together. Help me before I learn a bad habit.
I'd modify this to:
public ActionResult Index(string categoryName)
{
AuctionRepository auctionRepo = new AuctionRepository();
var auctions=auctionRepo.FindAllAuctions();
if (!string.IsNullOrEmpty(categoryName))
{
auctions = auctions.Where(c => c.Subcategory.Category.Name == categoryName);
}
return View(auctions);
}
Your route might then look like:
context.MapRoute(
"auction_defalt",
"Auction/{categoryName}",
new { controller="Auction", action = "Index", categoryName = UrlParameter.Optional }
Since the actions are so similar, I don't see a reason to separate them.
Like any framework ASP.NET MVC gives you plenty of opportunities to shoot yourself in the foot. Without forethought the reuse of controller actions, view models, and views can quickly become a maintenance nightmare. Not to mention that without similar consideration your routes will become hard to tie together.
Following the tenets of convention over configuration you could solve your problem by using separate actions but reusing a view partial. To me the index action of the AuctionsController should be responsible for listing all Auctions in the system. I wouldn't call my category action AuctionCategoryListing, but would instead call it simply Category. Through convention this has the nice effect of laying out the routes as:
site.com/auctions/ for the index
site.com/auctions/category/CATEGORYNAME for the category.
The route is easily understandable by the user and easy for you to understand explicitly what each does. (To this point Omar provides a good suggestion in his answer to let your repository handle pagination, filtering etc.)
As far as what each action should return you have several options. My preference would be to return separate views each containing a reference to a common partial. This gives you flexibility to create different views surrounding the partial but provides reuse for the piece that is common.
Further reading that might be of help:
One ViewModel per View
ASP.NET MVC terminology is tripping me up - why 'ViewModel'?
when should i use partial views in asp.net mvc?
You have to do branching somewhere, so it's probably more of a preference question.
The way I would handle it is to have a single method, and have it take in the category name as the parameter. Since strings are nullable, if one is not specified it will be null. My one action method would probably look something like:
public ActionResult Index(string categoryName)
{
AuctionRepository auctionRepo = new AuctionRepository();
var auctions = auctionRepo.FindAllAuctions();
if(String.IsNullOrEmpty(categoryName) == false)
auctions = auctions.Where(c => c.Subcategory.Category.Name == categoryName);
return View(auctions);
}
I would prefer the repo to do things like filtering and pagination for the sake of performance and DRY concept
public ActionResult Index(string categoryName)
{
AuctionRepository auctionRepo = new AuctionRepository();
//Let the Repo Handle things like filtering and pagination, avoiding performance issues
var auctions = auctionRepo.FindAllAuctions(categoryName);
return View(auctions);
}
DAL should be responsible for this tasks.
It's a feature of MVC that the view and controller are independent. You could equally use shared or partial views for the same thing, if anything I'd say it's a good thing because you're writing re-usable code and making use of it.