Sorting, filtering, paging and summary of an app are quite important features. In this video, which is a continuation of the Microsoft tutorial, I present step-by-step how to do each one of these.
It is quite a large video, but I hope it is worthy. Out of the sorting, filtering, paging and summary, I have decided to present the sorting capabilities of the app here. The sorting is done in the Index method of the StudentController.cs. Thus, in the app, we have clickable Last Name and Enrollment Date:
The code, presenting the business logic of the sorting and the filtering, as they are both in the Index method is here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public async Task Index( string sortOrder, string currentFilter, string searchString, int? pageNumber) { ViewData["CurrentSort"] = sortOrder; ViewData["NameSortParam"] = String.IsNullOrEmpty(sortOrder) ? "nameDesc" : ""; ViewData["DateSortParam"] = sortOrder == "date" ? "dateDesc" : "date"; if (searchString != null) { pageNumber = 1; } else { searchString = currentFilter; } ViewData["CurrentFilter"] = searchString; var students = from s in _context.Students select s; if (!String.IsNullOrEmpty(searchString)) { students = students.Where(s => s.LastName.Contains(searchString) || s.FirstMidName.Contains(searchString)); } switch (sortOrder) { case "nameDesc": students = students.OrderByDescending(s => s.LastName); break; case "date": students = students.OrderBy(s => s.EnrollmentDate); break; case "dateDesc": students = students.OrderByDescending(s => s.EnrollmentDate); break; default: students = students.OrderBy(s => s.LastName); break; } int pageSize = 3; return View(await PaginatedList.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize)); } |
The idea is that we pass 4 parameters – sortOrder, currentFilter, searchString and pageNumber and the Index starts rendering the page correspondingly. If it is a “search” action and not a normal display action, it loads the first page, with the search results. If it is a search, it loads only the results on the search. Then it implements the order, if it is to be implemented. The rendering of the page in the Views>Students>Index.cshtml file is even more interesting:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
@model PaginatedList<ContosoUniversity.Models.Student> @{ ViewData["Title"] = "Index"; } <h2>Index</h2> <p> <a asp-action="Create">Create New</a> </p> <form asp-action="Index" method="get"> <div class="form-action no-color"> <p> Find by name:<input type="text" name="searchString" value="@ViewData["CurrentFilter"]"/> <input type ="submit" value="Search" class="btn btn-success"/> <a asp-action="Index"> Back to Full List</a> </p> </div> </form> <table class="table"> <thead> <tr> <th> <a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParam"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Last Name</a> </th> <th> First Name </th> <th> <a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParam"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Enrollment Date</a> </th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.DisplayFor(modelItem => item.FirstMidName) </td> <td> @Html.DisplayFor(modelItem => item.EnrollmentDate) </td> <td> <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> | <a asp-action="Details" asp-route-id="@item.Id">Details</a> | <a asp-action="Delete" asp-route-id="@item.Id">Delete</a> </td> </tr> } </tbody> </table> @{ var prevDisabled = !Model.HasPreviousPage ? "disabled" : ""; var nextDisabled = !Model.HasNextPage ? "disabled" : ""; } <a asp-action="Index" asp-route-sortOrder ="@ViewData["CurrentSort"]" asp-route-pageNumber ="@(Model.PageIndex-1)" asp-route-currentFilter = "@ViewData["CurrentFilter"]" class="btn btn-default @prevDisabled"> Previous </a> <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex+1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @nextDisabled"> Next </a> |
There the filter is present at the beginning. The last part of the rendering are the arrows, which are used for Next and Previous. These are accessing the class PaginatedList.cs and using its corresponding properties to assign values of preDisabled and nextDisabled .
The PaginatedList.cs class and the whole code is available in GitHub here: https://github.com/Vitosh/ASP/tree/master/EFCoreAsp.NetMvcWebApp/ContosoUniversity003
Enjoy!