I have a view which I show result of a search and sort in a paged list. I add column headers as links to be enable the user to sort based on a column this way:
<tr>
<th>
@Html.ActionLink("Reference No", "Index",
new { sortOrder = ViewBag.RefNoSortParm, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
@Html.ActionLink("Category", "Index",
new { sortOrder = ViewBag.CatSortParm, currentFilter = ViewBag.CurrentFilter })
</th>
... Some other columns
</tr>
Also I added a pager this way:
Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
@Html.PagedListPager(Model, page => Url.Action("Index",
new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
And the action which I use looks like this:
private Registry_fssEntities db = new Registry_fssEntities();
public ActionResult Index(string sortOrder, SearchTransacModel searchModel, SearchTransacModel currentFilter, int? page)
{
var model = from c in db.TRANSACs
select c;
ViewBag.CurrentSort = sortOrder;
ViewBag.DispDateSortParm = String.IsNullOrEmpty(sortOrder) ? "DispDate" : "";
ViewBag.RefNoSortParm = sortOrder == "RefNo" ? "RefNo_desc" : "RefNo";
ViewBag.CatSortParm = sortOrder == "Cat" ? "Cat_desc" : "Cat";
if (searchModel.DATEDISPFROM != null || searchModel.DATEDISPTO != null || searchModel.DATEDISPFROM != null || searchModel.OFFICERNAME != null || searchModel.APPNAME != null || searchModel.REFNO != null || searchModel.PROCNAME != null)
{
page = 1;
}
else
{
searchModel = currentFilter;
}
if (searchModel != null)
{
if (!String.IsNullOrEmpty(searchModel.DATEDISPFROM))
{
ViewBag.DispFrom = searchModel.DATEDISPFROM.ToString();
}
else
{
searchModel.DATEDISPFROM = "01/01/" + DateTime.Today.Year.ToString();
ViewBag.DispFrom = "01/01/" + DateTime.Today.Year.ToString();
}
if (!String.IsNullOrEmpty(searchModel.DATEDISPTO))
{
ViewBag.DispTo = searchModel.DATEDISPTO.ToString();
}
else
{
searchModel.DATEDISPTO = "31/12/" + DateTime.Today.Year.ToString();
ViewBag.DispTo = "31/12/" + DateTime.Today.Year.ToString();
}
}
if (searchModel != null)
{
var tRANSACs = new TransacBusinessLogic();
model = tRANSACs.GetTransacs(searchModel);
ViewBag.currentFilter = searchModel;
}
List<TransacViewModel> TransactionList = new List<TransacViewModel>();
foreach (var item in model)
{
TransactionList.Add(new TransacViewModel {
TRANSID = item.TRANSID,
REFNO = item?.REFNO,
PROCESS = item?.PROCESS,
//CATEGORY = item.PROCESS.CATEGORY,
DOCTYPE = item?.DOCTYPE,
DATEDEL = returnasdate(item.DATEDEL),
DATEDISP = returnasdate(item.DATEDISP),
APPNAME = item?.APPNAME,
OFFICER = item?.OFFICER,
DATEREG = item.DATEREG
});
}
switch (sortOrder)
{
case "DispDate":
TransactionList = TransactionList.OrderBy(x => x.DATEDISP).ToList();
break;
case "RefNo":
TransactionList = TransactionList.OrderBy(t => t.REFNO).ToList();
break;
case "RefNo_desc":
TransactionList = TransactionList.OrderByDescending(t => t.PROCESS.CATEGORY.DETAIL).ToList();
break;
case "Cat":
TransactionList = TransactionList.OrderBy(t => t.PROCESS.CATEGORY.DETAIL).ToList();
break;
case "Cat_desc":
TransactionList = TransactionList.OrderByDescending(t => t.REFNO).ToList();
break;
default:
TransactionList = TransactionList.OrderByDescending(t => t.DATEDISP).ToList();
break;
}
int pageSize = 6;
int pageNumber = (page ?? 1);
return View(TransactionList.ToPagedList(pageNumber, pageSize));
}
SearchTransacModel
is a model used to contain my search parameters as I pass them to the controller.
It works fine when I submit the search form via the submit button, and I am sending this search criteria back to the view using a ViewBag
like this: ViewBag.currentFilter = searchModel;
But when I click any of the sort action links I lose the search parameters. I.e currentFilter
is null when I get to the controller.
I am passing currentFilter as query string and assigning the search model to it like in this case:
@Html.ActionLink("Reference No", "Index", new { sortOrder = ViewBag.RefNoSortParm, currentFilter = ViewBag.CurrentFilter })
Can anyone help please? I am new at MVC btw.
You can use such action:
public ActionResult Index(SearchModel searchModel, string sortColumn, string sortOrder)
{
ViewBag.SearchModel = searchModel;
ViewBag.SortColumn= sortColumn;
ViewBag.SortOrder = sortOrder;
var business = new BusinessLogic();
var model = business.Search(searchModel, sortColumn, sortOrder);
return View(model);
}
You can simply mix some route values this way. This way, both search options and sort options will be preserved between requests:
@{
var routeValues = new RouteValueDictionary(ViewBag.SearchModel ?? new { });
routeValues["sortColumn"] = ViewBag.SortColumn;
routeValues["sortOrder"] = ViewBag.SortOrder;
}
@Html.ActionLink("Link Text", "Action", routeValues);
Note:
Based on the idea in this answer you can simply create a method which mixes even 2 objects to create route values for you.
You also can encapsulate sort options in a SortOption
class and put it in ViewBag
and use it in model binding.
System.Linq.Dynamic
will help you to have a more dynamic and clean code for sort.
To learn more about benefits of such SearchModel
and BusinessLogic
technique take a look at Filter/Search using Multiple Fields - ASP.NET MVC
A common scenario for this requirement is for example, for page links of a pager or column links of a grid which is showing the result of a search and sort. If you are using this technique on a grid, paging links should preserve search options and sort options between requests. But for generating column header links, for each column you should use different suitable sort order and sort column; also for the column which its name equals to current sort column, the sort order should be inverse of current sort order. Also column headers should also preserve page index.