I have a web app that is written using ASP.NET MVC framework. In my Homecontroller
I have an action called Index
which responds to a Get
I think a better way should be passing back to the view the filters in ViewBag
.
You can make something like below:
@Html.PagedListPager(
Model.Tasks, id =>
Url.Action("Index", new { id,
Status = ViewBag.Status , AnotherFilterValue = ViewBag.AnotherFilterValue, ... }))
But keep in mind to test ViewBag.Status
for null value. If it does have a value put it in the route parameter list else set a default ActionLink
.
Then inside the POST action you expect a nullable int like below:
public ActionResult Index(int? id, int? status, ...)
{
int pageNumber = (id ?? 1);
if (ModelState.IsValid)
{
using(var connection = new Context())
{
if(status != null)
{
ViewBag.Status = status.value;
model.Tasks = connection.Tasks
.Where(task => task.Status == status.value)
.ToPagedList(pageNumber, 30);
}
else
{
model.Tasks = connection.Tasks
.ToPagedList(pageNumber, 30);
}
}
}
return View(model);
}
Your form needs to post back to the GET method, and that method needs to include parameters for your filter properties. Your PagedListPager
code in the view also need to include those filter properties so they are retained when you navigate to the next/previous page. Note that the Index()
POST method is not used and can be deleted.
Having your model contain a complex object to the filter properties and extra complexity when binding, so start by changing your model to
public class Presenter
{
public IPagedList<Task> Tasks { get; set; }
public int? Status { get; set; } // note nullable
... // add any other properties of TasksFiltersViewModel
public int PageNumber { get; set; }
public IEnumerable<SelectListItem> Statuses { get; set; }
}
Then change the Index()
method to
public ActionResult Index(int? id, int? status) // add any other parameters your filtering on
{
int pageNumber = (id ?? 1);
var tasks = db.Tasks; // IQueryable<Task>
if (status.HasValue)
{
tasks = tasks.Where(x => x.Status == status.Value)
}
if (otherParametersHaveValue)
{
tasks = tasks.Where(....);
}
Presenter model = new Presenter()
{
PageNumber = id ?? 1,
Status = status,
.... // set any other filter properties from the parameters
Statuses = new SelectList(...),
Tasks = tasks.ToPagedList(pageNumber, 30)
};
return View(model );
}
and change the view to
// Make the form a GET
@using (Html.BeginForm("Index", "Tasks", FormMethod.Get, new { @class = "form-horizontal" }))
{
....
// Modify expression based on revised model properties
@Html.DropDownListFor(m => m.Status, Model.Statuses, ...)
}
....
// Add filter parameters to url so they are retained
@Html.PagedListPager(Model.Tasks, id => Url.Action("Index", new { id, status = Model.Status })) // add other filter properties as required
Two ways to do this.
Just set a cookie for the filter options. On the actions where filtering is required, all you need to do is read the cookies and filter accordingly.
I dislike cookies and sessions, and would avoid them. But sometimes it could turn out to be what you need.
In your example you've used POST
to filter. Each time you click on a link, it fires a GET
request, not a POST
. So the filtering doesn't happen. The tricky part is making sure the GET
parameters are set each time. Could create a custom extension similar to Html.Action()
to start with. If you'll be checking the filter options in multiple actions, consider using an action Filter
.