Practical Approach to Learn MVC: Part 5

In this article you will learn the basics of customization of the Index view.

Introduction
 
This is the continuation from Part 4. If you are new to MVC then I will strongly recommend you to please go through Part Four before proceeding to this article. Click the following links for the previous parts of this tutorial.
In the last tutorial we see how to edit rows and add new rows from an existing table using the Code First Approach of Entity Framework and create views. In this session will discuss various customizations based on our requirements of the Index view.
 
In this session we will implement the following customization in the default Index view.
  • Search
  • Conductional Search
  • Paging
  • Shorting
Implementation of search in Index View
 
index 
 
For getting the preceding user interface we need to add a form tag inside the Index view just below the Create New form. The form tag contains one TextBox and one button with a label. Use the following code for this purpose.
  1. @using (Html.BeginForm("Index""Employees", FormMethod.Get))  
  2. {  
  3.     <text >Employee Name</text>  
  4.     <input id="tbtEmployeeName" type="text" name="EmpName" />  
  5.     <input id="Submit1" type="submit" value="submit" />  
  6. }  
We use @ symbol to switch razor to C# mode. The BeginForm html helper has some overloaded form in which we select the preceding form that contains three parameters. The syntax will be like follows:
  1. Html.BeginForm("<Controller Action Method Name>""<ControllerName>", FormMethod.Get)  
The form method will be Get in this case. That's it, our user interface is ready. Now we need to change the Index action method of the Employees Controller. Use the following code to do that:
  1. // GET: Employees  
  2.         public ActionResult Index(string EmpName)  
  3.         {  
  4.              
  5.             if (string.IsNullOrEmpty(EmpName))  
  6.             {  
  7.                 return View (db.Employees.Include(e => e.Department).ToList());  
  8.             }  
  9.             else  
  10.             {  
  11.                 return View (db.Employees.Include(e => e.Department)  
  12.                     .Where (x=>x.EmpName.StartsWith(EmpName)).ToList ());  
  13.             }              
  14.         }  
This is simple and straightforward code that I have written. A simple LINQ query I have used to filter the employees whose name starts with the name provided by the user.
 
Note: Here one thing should be kept in mind. That is, the name of the TextBox and the parameter name of the Index action method must be the same. In this case it's EmpName. So that the MVC model binder can understand what value is passing from the view. Suppose we have more than one TextBox for a search. Then there must be a same name parameter available in that method otherwise it will return an error.
 
Here if we want to add a message for no row found if no employee name matches the search criteria then we need to modify the table like:
  1. <table class="table">  
  2.     <tr>  
  3.         <th>  
  4.             @Html.DisplayNameFor(model => model.EmpName)  
  5.         </th>  
  6.         <th>  
  7.             @Html.DisplayNameFor(model => model.Salary)  
  8.         </th>  
  9.         <th>  
  10.             @Html.DisplayNameFor(model => model.Gender)  
  11.         </th>  
  12.         <th>  
  13.             @Html.DisplayNameFor(model => model.MobileNo)  
  14.         </th>  
  15.         <th>  
  16.             @Html.DisplayNameFor(model => model.EmailId)  
  17.         </th>  
  18.         <th>  
  19.             @Html.DisplayNameFor(model => model.EmpDOB)  
  20.         </th>  
  21.         <th>  
  22.             @Html.DisplayNameFor(model => model.Department.DeptName)  
  23.         </th>  
  24.         <th>Action</th>  
  25.     </tr>  
  26.     @if (Model.Count() == 0)  
  27.     {  
  28.         <tr>  
  29.             <td colspan="8"><text>No Data Found.</text></td>  
  30.         </tr>  
  31.     }  
  32.     else  
  33.     {  
  34.         foreach(var item in Model)  
  35.         {  
  36.             <tr>  
  37.   
  38.                 <td>  
  39.                     @Html.DisplayFor(modelItem => item.EmpName)  
  40.                 </td>  
  41.                 <td>  
  42.                     @Html.DisplayFor(modelItem => item.Salary)  
  43.                 </td>  
  44.                 <td>  
  45.                     @Html.DisplayFor(modelItem => item.Gender)  
  46.                 </td>  
  47.                 <td>  
  48.                     @Html.DisplayFor(modelItem => item.MobileNo)  
  49.                 </td>  
  50.                 <td>  
  51.                     @Html.DisplayFor(modelItem => item.EmailId)  
  52.                 </td>  
  53.                 <td>  
  54.                     @Html.DisplayFor(modelItem => item.EmpDOB)  
  55.                 </td>  
  56.                 <td>  
  57.                     @Html.DisplayFor(modelItem => item.Department.DeptName)  
  58.                 </td>  
  59.                 <td>  
  60.                     @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |  
  61.                     @Html.ActionLink("Details", "Details", new { id = item.Id }) |  
  62.                     @Html.ActionLink("Delete", "Delete", new { id = item.Id })  
  63.                 </td>  
  64.             </tr>  
  65.         }  
  66.     }  
  67.   
  68. </table>  
I have just added an if condition that checks if the model does not contain any row and renders a simple message there. Here we need to remove the foreach(var item in Model) @ symbol because all the cs code is available inside one if block.
 
Implementation of Conductional Search
 
Implementation of Conductional Search  
 
Now our requirement has been modified as you can see from the preceding image. We need to make our search screen more flexible by adding a search by department option also. So for doing this we need to modify our form element with the following code.
  1. @using (Html.BeginForm("Index""Employees", FormMethod.Get))  
  2. {  
  3.     <text><b>Search By : </b></text>  
  4.     @Html.RadioButton("searchBy""Name")<text>Name</text>  
  5.     @Html.RadioButton("searchBy","Department")<text>Department</text>  
  6.     <br />  
  7.     <input id="txtsearchTerm " type="text" name="searchTerm" />  
  8.     <input id="btnsubmit" type="submit" value="submit" />  
  9. }  
Now run the index view and notice the URL by selecting one option with some data in the text box. The URL will be something like.
 
http://localhost/CodeFirstApproachWithEmployeesInfo/Employees?searchBy=Name&EmpName=m
 
Here I select the name radio button and enter “m” inside TextBox. searchBy=Name&EmpN here searchBy is passing a value Name so inside the Index action method we need to find this parameter value and modify it. After modification the index action method will be like:
  1. // GET: Employees  
  2.       public ActionResult Index(string searchTerm, string searchBy)  
  3.       {  
  4.           if (searchBy=="Name")  
  5.           {  
  6.               return View(db.Employees.Include(e => e.Department).Where(  
  7.                   x => x.EmpName.StartsWith(searchTerm) || searchTerm==null).ToList());  
  8.           }  
  9.           else  
  10.           {  
  11.               return View(db.Employees.Include(e => e.Department)  
  12.                   .Where(x => x.Department.DeptName.StartsWith(searchTerm)||searchTerm==null).ToList());  
  13.                 
  14.           }                        
  15.       }  
This is a straightforward LINQ query. One thing should always be kept in mind. The name of the parameter must match the user interface tag name otherwise you will get an error.
 
Note: If you want to make the name radio button selected by default when the Index view loads then we need to just pass one additional Boolean flag to that radio button's HTML helper.
  1. @Html.RadioButton("searchBy""Name",true)<text>Name</text>  
Implementation of Paging in Index view
 
For implementing the paging control we need to add the PagedList.MVC package. For adding PagedList.MVC right-click on the references tab and click on Manage NuGet packages and search PagedList.MVC.
 
pagedlist 
 
Click Install to install that package. Then use the following procedure.
 
Step 1
 
After successful installation go to the controller action method for the index view and import the following namespace:
  1. using PagedList;  
Step 2
 
Pass the page number parameter in the Index action method.
  1. public ActionResult Index(string searchTerm, string searchBy, int? page)  
  2. {  
  3. }  
Page is of Nullable int because first the time value will be null.
 
Step 3
 
Modify the preceding method using the following code.
  1. public ActionResult Index(string searchTerm, string searchBy, int? page)  
  2. {  
  3.      if (searchBy=="Name")  
  4.      {  
  5.          return View(db.Employees.Include(e => e.Department).Where(  
  6.              x => x.EmpName.StartsWith(searchTerm) || searchTerm==null).ToList()  
  7.              .ToPagedList(page ?? 1, 3));  
  8.      }  
  9.      else  
  10.      {  
  11.          return View(db.Employees.Include(e => e.Department)  
  12.              .Where(x => x.Department.DeptName.StartsWith(searchTerm)||searchTerm==null).ToList()  
  13.              .ToPagedList(page ?? 1, 3));  
  14.            
  15.      }                        
  16.  }  
Now hover the mouse over return to see which type of list it is returning.
 
cs code 
 
It's of type IpagedList<Employee>.
 
Step 4
 
As we know the return type of the view is of type IpagedList<Employee> but currently our Employee view is of type IEnumerable so we need to modify that.
  1. @model PagedList.IPagedList<CodeFirstApproachWithEmployeesInfo.Models.Employee>  
We need to add the following namespace also.
  1. @using PagedList.Mvc;  
Step 5
 
Now the view is of type IPagedList< Employee > so for accessing the list value from the type IPagedList type we need to use model.First().EmpName and so on.
 
Step 6
 
Now we need to write the following code to display the page list inside the Index view.
  1. @Html.PagedListPager(Model, page => Url.Action("Index"new { page, searchBy =   
  2. Request.QueryString["searchBy"], search = Request.QueryString["search"] }))  
There might be some confusion in the preceding code so let me explain it a bit.
 
Request.QueryString["searchBy"],search = Request.QueryString["search"] if we did not pass this parameter then when we move to the second page we will lose the filter condition. If you are not using more than one condition then you may skip this code.
 
The final Index view code will be like:
  1. @model PagedList.IPagedList<CodeFirstApproachWithEmployeesInfo.Models.Employee>  
  2. @using PagedList.Mvc;  
  3. @{  
  4.     ViewBag.Title = "Index";  
  5. }  
  6.   
  7. <h2>Index</h2>  
  8.   
  9. <p>  
  10.     @Html.ActionLink("Create New""Create")  
  11. </p>  
  12. @using (Html.BeginForm("Index""Employees", FormMethod.Get))  
  13. {  
  14.     <text><b>Search By : </b></text>  
  15.     @Html.RadioButton("searchBy""Name"true)<text>Name</text>  
  16.     @Html.RadioButton("searchBy""Department")<text>Department</text>  
  17.     <br />  
  18.     <input id="tbtsearchTerm" type="text" name="searchTerm" />  
  19.     <input id="btnsubmit" type="submit" value="submit" />  
  20. }  
  21. <br />  
  22. <table class="table">  
  23.     <tr>  
  24.         <th>  
  25.             @Html.DisplayNameFor(model => model.First().EmpName)  
  26.         </th>  
  27.         <th>  
  28.             @Html.DisplayNameFor(model => model.First().Salary)  
  29.         </th>  
  30.         <th>  
  31.             @Html.DisplayNameFor(model => model.First().Gender)  
  32.         </th>  
  33.         <th>  
  34.             @Html.DisplayNameFor(model => model.First().MobileNo)  
  35.         </th>  
  36.         <th>  
  37.             @Html.DisplayNameFor(model => model.First().EmailId)  
  38.         </th>  
  39.         <th>  
  40.             @Html.DisplayNameFor(model => model.First().EmpDOB)  
  41.         </th>  
  42.         <th>  
  43.             @Html.DisplayNameFor(model => model.First().Department.DeptName)  
  44.         </th>  
  45.         <th>Action</th>  
  46.     </tr>  
  47.     @if (Model.Count() == 0)  
  48.     {  
  49.         <tr>  
  50.             <td colspan="8"><text>No Data Found.</text></td>  
  51.         </tr>  
  52.     }  
  53.     else  
  54.     {  
  55.         foreach (var item in Model)  
  56.         {  
  57.             <tr>  
  58.   
  59.                 <td>  
  60.                     @Html.DisplayFor(modelItem => item.EmpName)  
  61.                 </td>  
  62.                 <td>  
  63.                     @Html.DisplayFor(modelItem => item.Salary)  
  64.                 </td>  
  65.                 <td>  
  66.                     @Html.DisplayFor(modelItem => item.Gender)  
  67.                 </td>  
  68.                 <td>  
  69.                     @Html.DisplayFor(modelItem => item.MobileNo)  
  70.                 </td>  
  71.                 <td>  
  72.                     @Html.DisplayFor(modelItem => item.EmailId)  
  73.                 </td>  
  74.                 <td>  
  75.                     @Html.DisplayFor(modelItem => item.EmpDOB)  
  76.                 </td>  
  77.                 <td>  
  78.                     @Html.DisplayFor(modelItem => item.Department.DeptName)  
  79.                 </td>  
  80.                 <td>  
  81.                     @Html.ActionLink("Edit""Edit"new { id = item.Id }) |  
  82.                     @Html.ActionLink("Details""Details"new { id = item.Id }) |  
  83.                     @Html.ActionLink("Delete""Delete"new { id = item.Id })  
  84.                 </td>  
  85.             </tr>  
  86.         }  
  87.     }  
  88.   
  89. </table>  
  90. @Html.PagedListPager(Model, page => Url.Action("Index"new  
  91. {  
  92.     page,  
  93.     searchBy = Request.QueryString["searchBy"],  
  94.     search = Request.QueryString["search"]  
  95. }),  
  96. new PagedListRenderOptions()  
  97. {  
  98.     Display = PagedListDisplayMode.IfNeeded,  
  99.     DisplayItemSliceAndTotal = true  
  100. })  
Now run the Index view to get the following results.
 
search 
 
Now the problem with the preceding code is that when only three records will display then also the page number will. So how to modify page number to be shown when a second page of records exist else no?
  1. @Html.PagedListPager(Model, page => Url.Action("Index"new   
  2. {   
  3.     page,   
  4.     searchBy = Request.QueryString["searchBy"],  
  5.     search = Request.QueryString["search"]  
  6. }),   
  7. new PagedListRenderOptions()   
  8. {   
  9.     Display = PagedListDisplayMode.IfNeeded   
  10. })   
So now the page list will display only if there are more records than will be in the table.
 
create new 
 
Note: PagedListRenderOptions() is one of the very important property to edit the design of paging.
 
There are several parameters or flags available for various purposes.
 
design of paging 
 
You can use it for various purposes.
  1. If you want to display the current active page and the total number of pages.

    Just make the following flag true.
    1. DisplayPageCountAndCurrentLocation = true  
    Now the paging code will be like:
    1. @Html.PagedListPager(Model, page => Url.Action("Index"new   
    2. {   
    3.     page,   
    4.     searchBy = Request.QueryString["searchBy"],  
    5.     search = Request.QueryString["search"]  
    6. }),   
    7. new PagedListRenderOptions()   
    8. {   
    9.     Display = PagedListDisplayMode.IfNeeded,  
    10.     DisplayPageCountAndCurrentLocation = true  
    11. })  
    And the output result for that page will be:

    detail

  2. If you want to display the number of rows displayed, of the total number of rows available.
    Mark following flag true instead of the preceding flag:
    1. DisplayItemSliceAndTotal = true  
    The code will be like:
    1. new PagedListRenderOptions()   
    2. {   
    3.    Display = PagedListDisplayMode.IfNeeded,  
    4.    DisplayItemSliceAndTotal = true  
    5. })  
    And the output will be:

    show item

    In the same way you can use the other properties, just check the type of value that the flag is accepting and use it.

    Implementation of Shorting controls in Index view.

    Now we try to implement sorting in the preceding table. When we click department the first time it will sort the department by salary the first time in ascending order. If I click again then the records will be sorted in descending order. Let's see how to implement this.

    For implementing sorting we need to use the following procedure.

    Step 1

    Go to the Index action method inside the Employees controller and modify the method using the following code.
    1. public ActionResult Index(string searchTerm, string searchBy, int? page, string sortBy)  
    2. {  
    3.     ViewBag.DeptSortParm = String.IsNullOrEmpty(sortBy) ? "DeptDesc" : "";  
    4.     ViewBag.NameSortParm = sortBy == "EmpName" ? "EmpNameDesc" : "EmpName";  
    5.     var employees = db.Employees.AsQueryable();  
    6.   
    7.     if (searchBy == "DeptDesc")  
    8.     {                  
    9.         employees = employees.Include(e => e.Department)  
    10.               .Where(x => x.Department.DeptName.Equals(searchTerm) || searchTerm == null);  
    11.   
    12.     }  
    13.     else  
    14.     {  
    15.         employees = employees.Where(x => x.EmpName.Equals(searchTerm) || searchTerm == null);  
    16.     }  
    17.     switch (sortBy)  
    18.     {  
    19.         case "DeptDesc":  
    20.             employees = employees.OrderByDescending(x => x.Department.DeptName);  
    21.             break;  
    22.         case "EmpNameDesc":  
    23.             employees = employees.OrderByDescending(x => x.EmpName);  
    24.             break;  
    25.         case "EmpName":  
    26.             employees = employees.OrderBy(x => x.EmpName);  
    27.             break;  
    28.         default:  
    29.             employees = employees.OrderBy(x => x.Department.DeptName);  
    30.             break;  
    31.     }  
    32.     return View(employees.ToPagedList(page ?? 1, 3));           
    33. }  
    Step 2

    Now our Index action method is ready. In the view we need to create two link buttons on the department and Empname header text. When we click on that button sorting will be in action. For generating a link button we need to use the following HTML helper.

    Replace the Employee name field with the following code:
    1. @Html.ActionLink("Name""Index"new  
    2.         {  
    3.             sortBy = ViewBag.NameSortParm,  
    4.             searchBy = Request["searchBy"],  
    5.             search = Request["search"]  
    6.         })    
    And replace the department with the following code:
    1. @Html.ActionLink("Department""Index"new   
    2.         {  
    3.             sortBy = ViewBag.DeptSortParm,   
    4.             searchBy = Request["searchBy"],   
    5.             search = Request["search"]   
    6.         })  
    That's it, sorting is ready. Run the Index view and you will get the following results.

    name

    As shown in the preceding image we are getting the hyperlink for the name and department field. When we click on that we will get the output as we want.

    Note: The problem with this code is that when we clicked on page number two we lost our filter condition. This is basically because when we click on the page the page the URL is changed.

    One page number one after sorting the URL is (click on the Name for example).

    http://localhost/CodeFirstApproachWithEmployeesInfo/Employees?sortBy=EmpName

    But when we click on the page two the URL will be:

    http://localhost/CodeFirstApproachWithEmployeesInfo/Employees?page=2

    Look at the difference on page two. We are losing our shortBy parameter. So we lost our sort results. For maintaining the sort results show how we need to pass the shortBy parameter on page two also. So for doing this we need to modify our paging code a bit like by adding:

    sortBy = Request["sortBy"]

    So the final code will be:
    1. @Html.PagedListPager(Model, page => Url.Action("Index"new  
    2. {  
    3.     page,  
    4.     searchBy = Request.QueryString["searchBy"],  
    5.     search = Request.QueryString["search"],  
    6.     sortBy = Request["sortBy"]   
    7. }),  
    8. new PagedListRenderOptions()  
    9. {  
    10.     Display = PagedListDisplayMode.IfNeeded,  
    11.     DisplayItemSliceAndTotal = true  
    12. })  
    That's it, we are now all done. The sorting will work fine. The index view will look like:
    1. @using PagedList.Mvc;  
    2. @{  
    3.     ViewBag.Title = "Index";  
    4. }  
    5.   
    6. <h2>Index</h2>  
    7.   
    8. <p>  
    9.     @Html.ActionLink("Create New""Create")  
    10. </p>  
    11. @using (Html.BeginForm("Index""Employees", FormMethod.Get))  
    12. {  
    13.     <text><b>Search By : </b></text>  
    14.     @Html.RadioButton("searchBy""Name"true)<text>Name</text>  
    15.     @Html.RadioButton("searchBy""Department")<text>Department</text>  
    16.     <br />  
    17.     <input id="tbtsearchTerm" type="text" name="searchTerm" />  
    18.     <input id="btnsubmit" type="submit" value="submit" />  
    19. }  
    20. <br />  
    21. <table class="table">  
    22.     <tr>  
    23.         <th>  
    24.             @Html.ActionLink("Name""Index"new  
    25.             {  
    26.                 sortBy = ViewBag.NameSortParm,  
    27.                 searchBy = Request["searchBy"],  
    28.                 search = Request["search"]  
    29.             })  
    30.   
    31.         </th>  
    32.         <th>  
    33.             @Html.DisplayNameFor(model => model.First().Salary)  
    34.         </th>  
    35.         <th>  
    36.             @Html.DisplayNameFor(model => model.First().Gender)  
    37.         </th>  
    38.         <th>  
    39.             @Html.DisplayNameFor(model => model.First().MobileNo)  
    40.         </th>  
    41.         <th>  
    42.             @Html.DisplayNameFor(model => model.First().EmailId)  
    43.         </th>  
    44.         <th>  
    45.             @Html.DisplayNameFor(model => model.First().EmpDOB)  
    46.         </th>  
    47.         <th>  
    48.             @Html.ActionLink("Department""Index"new  
    49.             {  
    50.                 sortBy = ViewBag.DeptSortParm,  
    51.                 searchBy = Request["searchBy"],  
    52.                 search = Request["search"]  
    53.             })  
    54.   
    55.         </th>  
    56.         <th>Action</th>  
    57.     </tr>  
    58.     @if (Model.Count() == 0)  
    59.     {  
    60.         <tr>  
    61.             <td colspan="8"><text>No Data Found.</text></td>  
    62.         </tr>  
    63.     }  
    64.     else  
    65.     {  
    66.         foreach (var item in Model)  
    67.         {  
    68.             <tr>  
    69.   
    70.                 <td>  
    71.                     @Html.DisplayFor(modelItem => item.EmpName)  
    72.                 </td>  
    73.                 <td>  
    74.                     @Html.DisplayFor(modelItem => item.Salary)  
    75.                 </td>  
    76.                 <td>  
    77.                     @Html.DisplayFor(modelItem => item.Gender)  
    78.                 </td>  
    79.                 <td>  
    80.                     @Html.DisplayFor(modelItem => item.MobileNo)  
    81.                 </td>  
    82.                 <td>  
    83.                     @Html.DisplayFor(modelItem => item.EmailId)  
    84.                 </td>  
    85.                 <td>  
    86.                     @Html.DisplayFor(modelItem => item.EmpDOB)  
    87.                 </td>  
    88.                 <td>  
    89.                     @Html.DisplayFor(modelItem => item.Department.DeptName)  
    90.                 </td>  
    91.                 <td>  
    92.                     @Html.ActionLink("Edit""Edit"new { id = item.Id }) |  
    93.                     @Html.ActionLink("Details""Details"new { id = item.Id }) |  
    94.                     @Html.ActionLink("Delete""Delete"new { id = item.Id })  
    95.                 </td>  
    96.             </tr>  
    97.         }  
    98.     }  
    99.   
    100. </table>  
    101. @Html.PagedListPager(Model, page => Url.Action("Index"new  
    102. {  
    103.     page,  
    104.     searchBy = Request.QueryString["searchBy"],  
    105.     search = Request.QueryString["search"],  
    106.     sortBy = Request["sortBy"]  
    107. }),  
    108. new PagedListRenderOptions()  
    109. {  
    110.     Display = PagedListDisplayMode.IfNeeded,  
    111.     DisplayItemSliceAndTotal = true  
    112. })  
Summary
 
In this illustration you came to understand the basics of customization on the Index view.
 
Thanks.
 
I would like to get feedback from my readers. Please post your feedback, question, or comments about this article.