I created a simple application where I have a product list and search filter sidebar.(https://i.sstatic.net/pXwss.png)
I had a problem with pagination(using PagedList
) because after filtering results and moving on next page(https://i.sstatic.net/8qSnA.png) I was getting an empty page. Reason was clear. I was getting data in controller action parameters, so after clicking next page button, filter data was lost. So I decided to store this data somewhere.
I was told that I needed ViewModel, so I created another model FilterModel
with side bar fields:
public class FilterModel {
public string Manufacturer { get; set; }
public string Name { get; set; }
public int? MinPrice { get; set; }
public int? MaxPrice { get; set; }
}
and this is Products
model
public partial class Products {
[Key]
public int ProductID { get; set; }
public string Manufacturer { get; set; }
public string Name { get; set; }
......
public int Price { get; set; }
}
And put them in ViewModel
:
public class HomeViewModel {
public IPagedList<Products> Repository { get; set; }
public FilterModel FilterView { get; set; }
}
these are my controllers - HomeController
that displays every product and SearchFilterController
that should display filtered products:
public class HomeController : Controller {
IProductRepository _repository;
public HomeController(IProductRepository rep) {
_repository = rep;
}
public ActionResult Index(int page=1) {
ViewBag.Manufacturers = Manufacturers.ManufacturersList(_repository);
var model =
(from p in _repository.Product
orderby p.Price descending
select p).ToPagedList(page, 2);
ViewBag.Title = "Home";
return View(model);
}
}
.
public class SearchFilterController : Controller {
IProductRepository _repository;
public SearchFilterController(IProductRepository rep) {
_repository = rep;
}
[HttpPost]
public ActionResult Filter(FilterModel filterModel, int page = 1) {
ViewBag.Manufacturers = Manufacturers.ManufacturersList(_repository);
var model =
(from p in _repository.Product
where (p.Manufacturer == filterModel.Manufacturer || p.Name.Contains(filterModel.Name) ||
p.Price >= filterModel.MinPrice || p.Price <= filterModel.MaxPrice)
orderby p.Price descending
select p).ToPagedList(page, 1);
ViewBag.Title = "Results";
return View(model);
}
}
In both controllers I pass views
PagedList<Products>
model but they should be getting HomeViewModel
I guess.
these are my views:
Index
(HomeController
)
@model SmartPhoneCatalog.Models.HomeViewModel
@using System.Linq
@using PagedList
@using PagedList.Mvc
@using SmartPhoneCatalog.Domain.Abstract
...
<div id="root">
@Html.Partial("_Sidebar", Model)
@Html.Partial("_ProductsList", Model)
</div>
<br>
<div class="pagedList" data-sc-target="#products">
@Html.PagedListPager(Model.Repository, page => Url.Action("Index", "Home", new { page }),
PagedListRenderOptions.ClassicPlusFirstAndLast);
</div>
Filter
(SearchFilterController
)
@model SmartPhoneCatalog.Models.HomeViewModel
@using System.Linq
@using PagedList
@using PagedList.Mvc
@using SmartPhoneCatalog.Domain.Abstract
...
<div id="root">
@Html.Partial("_Sidebar", Model)
@Html.Partial("_ProductsList", Model)
</div>
<br>
<div class="pagedList" data-sc-target="#products">
@Html.PagedListPager(Model.FilterModel, page => Url.Action("Filter", "SearchFilter", new { page }),
PagedListRenderOptions.ClassicPlusFirstAndLast);
</div>
Partial View:
_Sidebar
@model SmartPhoneCatalog.Models.HomeViewModel
@using System.Linq
@using PagedList
@using PagedList.Mvc
<div id="filter" class="left">
@using (Html.BeginForm("Filter", "SearchFilter")) {
<div>
<b>Manufacturer:</b> <br>
<select name="manufacturer" class="form-control">
<option>@null</option>
@foreach (var item in ViewBag.Manufacturers) {
<option>@item</option>
}
</select><br>
<b>Name:</b> <br>@Html.EditorFor(model=>model.FilterModel.Name)<br>
<b>Price From:</b> <br>@Html.EditorFor(model => model.FilterModel.Name)<br>
<b>To:</b> <br>@Html.EditorFor(model => model.FilterModel.Name)<br>
<button type="submit" value="search"><b>Search</b></button>
</div>
}
</div>
I think I have everything done right in views. but I don't know how to pass them models correclty and object binding is done right or not.
Any suggestions?
Edit: OK, I did this in Controllers: Created HomeViewModel
object and changed this
var model =
(from p in _repository.Product
orderby p.Price descending
select p).ToPagedList(page, 2);
return View(model);
into this:
_homeView.Repository =
(from p in _repository.Product
orderby p.Price descending
select p).ToPagedList(page, 2);
return View(_homeView);
now everything works except the pagination after filtering results(what was the reason why I did all of this :D). So I have to fix model binding
hi I have executed your code.. below are the changes
Declare a Model like this
public class FilterModel
{
public string Manufacturer { get; set; }
public string Name { get; set; }
public int? MinPrice { get; set; }
public int? MaxPrice { get; set; }
}
public partial class Products
{
public int ProductID { get; set; }
public string Manufacturer { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
public class HomeViewModel
{
public IPagedList<Products> Repository { get; set; }
public FilterModel FilterView { get; set; }
}
Controller(s) action methods are
public class PagingController : Controller
{
int pagesize = 2;
public ActionResult Index(int page = 1)
{
HomeViewModel _HomeViewModel = new HomeViewModel();
//call service and get products list
AgentServiceReference.AgentServiceClient ServiceClient = new AgentServiceClient();
var productlist = ServiceClient.GetProducts().ToPagedList(page, pagesize);
//set IPagedList<Products> to our HomeViewModel Repository property
_HomeViewModel.Repository = productlist;
// set view bag this we use for Drop Down list
SetViewBags();
ViewBag.Title = "Home";
return View(_HomeViewModel);
}
[HttpGet]
public ActionResult Filter(int page = 1)
{
//call service and get products list
AgentServiceReference.AgentServiceClient ServiceClient = new AgentServiceClient();
var productlist = ServiceClient.GetProducts().ToPagedList(page, pagesize);
//set IPagedList<Products> to our HomeViewModel Repository property
HomeViewModel _homeViewModel = new HomeViewModel();
_homeViewModel.Repository = productlist;
SetViewBags();
return View("Index", _homeViewModel);
}
[HttpPost]
public ActionResult Search(HomeViewModel HomeViewModel)
{
HomeViewModel _homeviewModel = new HomeViewModel();
AgentServiceReference.AgentServiceClient ServiceClient = new AgentServiceClient();
var productlist = ServiceClient.GetProducts();
// do filter stuff here........
var resultpagedList = productlist.ToPagedList(3, pagesize);
_homeviewModel.Repository = resultpagedList;
SetViewBags();
return View("Index", _homeviewModel);
}
private void SetViewBags()
{
List<SelectListItem> manufactures = new List<SelectListItem>();
for (int i = 0; i < 10; i++)
{
manufactures.Add(new SelectListItem { Text = "Text" + i, Value = "Value" + i, Selected = false });
}
ViewBag.Manufacturers = manufactures;
}
}
Index.cshtml page
@using System.Linq
@using PagedList
@using PagedList.Mvc
@model EntityLayer.HomeViewModel
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm("Search", "Paging", FormMethod.Post))
{
<div id="root">
@Html.Partial("_Sidebar", Model)
@Html.Partial("_ProductsList", Model)
</div>
}
_Sidebar.cshtml
@model EntityLayer.HomeViewModel
@using System.Linq
@using PagedList
@using PagedList.Mvc
<div id="filter" class="left">
<div>
<b>Manufacturer:</b>
<br>
@Html.DropDownListFor(model => model.FilterView.Manufacturer, ViewBag.Manufacturers as IEnumerable<SelectListItem>)
<b>Name:</b>
<br>@Html.TextBoxFor(model => model.FilterView.Name)<br>
<b>Price From:</b>
<br>@Html.TextBoxFor(model => model.FilterView.MaxPrice)<br>
<b>To:</b>
<br>@Html.TextBoxFor(model => model.FilterView.MinPrice)<br>
<button type="submit" value="search"><b>Search</b></button>
</div>
and finally _ProductsList.cshtml
@model EntityLayer.HomeViewModel
@using System.Linq
@using PagedList
@using PagedList.Mvc
<table>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
@foreach (var item in Model.Repository)
{
<tr>
<td>@item.Name</td>
<td>@item.Price</td>
</tr>
}
<div class="pagedList" data-sc-target="#products">
@Html.PagedListPager(Model.Repository, page => Url.Action("Filter", "Paging", new { page }),
PagedListRenderOptions.ClassicPlusFirstAndLast);
Hope it helps you