0

I am looking to add several more search strings in my controller that will persist through paging in my view.

Currently, the parameter searchString can be set at the currentFilter and will persist as long as it is the only parameter. I'm looking to add several more (searchString2, searchString3, etc.) but those parameters drop out.

I am looking for a simple way to do this.

Student Index:

@model PagedList.IPagedList<ContosoUniversity.Models.Student>
@using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />

@{
    ViewBag.Title = "Students";
}

<h2>Students</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
    <p>
        Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
        <input type="submit" value="Search" />
    </p>
}
<table class="table">
    <tr>
        <th>
            @Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
        </th>
        <th>
            First Name
        </th>
        <th>
            @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, currentFilter = ViewBag.CurrentFilter })
        </th>
        <th></th>
    </tr>


    @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>
                @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
                @Html.ActionLink("Details", "Details", new { id = item.ID }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.ID })
            </td>
        </tr>
    }

</table>
<br />
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 }))

Student Controller GET:

// GET: Student
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
{
    ViewBag.CurrentSort = sortOrder;
    ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
    ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";

    if (searchString != null)
    {
        page = 1;
    }
    else
    {
        searchString = currentFilter;
    }

    ViewBag.CurrentFilter = searchString;

    var students = from s in db.Students orderby s.LastName
                   select s;

    if (!String.IsNullOrEmpty(searchString))
    {
        //students = students.Where(s => s.LastName.Contains(searchString)
        //                       || s.FirstMidName.Contains(searchString));
    }
    switch (sortOrder)
    {
        case "name_desc":
            students = students.OrderByDescending(s => s.LastName);
            break;
        case "Date":
            students = students.OrderBy(s => s.EnrollmentDate);
            break;
        case "date_desc":
            students = students.OrderByDescending(s => s.EnrollmentDate);
            break;
        default:  // Name ascending 
            students = students.OrderBy(s => s.LastName);
            break;
    }

    int pageSize = 3;
    int pageNumber = (page ?? 1);
    return View(students.ToPagedList(pageNumber, pageSize));
}
  • 1
    You just need to add textboxes in you view for `SearchString2` and `SearchString3` properties plus matching parameters in your GET method plus `ViewBag` properties to pass the values to the view, and route values in `PagedListPager` to pass them back (exactly as is done for your existing `SearchString`) –  Jun 03 '16 at 23:03
  • Hi. I don't quite understand how to change the code so that each place that has "searchString" is, instead, several strings. It also seems like it would get unwieldy with more parameters. Is there a way to put the parameters into a list or something, and then reference the child objects directly in place of searchString like list? I would also need to still reference it for my currentFilter parameter, so paging will continue to work. – Cocoa Krispie Kitty Jun 06 '16 at 16:29

2 Answers2

0

Although there are places you could tidy up in your code, ultimately your approach is correct. The string needs to pass backward and forward between the browser and server and this means either using URL params or a POST, either way MVC will bind it to a ViewModel (or to individual parameters in your case).

Any attempt to cache this on the server would require session handling which would be overly complex for your requirements.

PhillipH
  • 6,182
  • 1
  • 15
  • 25
  • I get what you are saying conceptually, but similar to my reply to Stephen Muecke above, I don't quite understand how to get the URL parameters to be used beyond the one that I currently have. The tutorials I've used glaze over that. http://stackoverflow.com/questions/5003953/asp-net-mvc-extract-parameter-of-an-url seems relevant, but I am still unclear on how to translate it to my case. The POST approach sounds promising, but that concept also seems beyond my current level of comprehension. – Cocoa Krispie Kitty Jun 06 '16 at 16:30
0

Below is what you should have in your View and Controller.
( See the necessary edits in your code, particularly in the URL and see the changes in the Controller to make the search items persist navigating multiple pages )

Your View should look like:

@model PagedList.IPagedList<ContosoUniversity.Models.Student>
@using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />

@{
   ViewBag.Title = "Students";
 }

 <h2>Students</h2>

 <p>
 @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
<p>
    Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
 Find by searchString2: @Html.TextBox("searchString2", ViewBag.CurrentFilter2 as string)
 Find by searchString3: @Html.TextBox("searchString3", ViewBag.CurrentFilter3 as string)
    <input type="submit" value="Search" />
</p>
}
 <table class="table">
 <tr>
    <th>
    @Html.ActionLink("Last Name", "Index", new { sortOrder =   ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    <th>
        First Name
    </th>
    <th>
    @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    <th></th>
 </tr>

  @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>
            @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
            @Html.ActionLink("Details", "Details", new { id = item.ID }) |
            @Html.ActionLink("Delete", "Delete", new { id = item.ID })
        </td>
     </tr>
  }

  </table>
  <br />
   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, currentFilter2 = ViewBag.CurrentFilter2, currentFilter3 = ViewBag.CurrentFilter3 }))

And your Controller may look something like: (depending your business needs)

// GET: Student
public ViewResult Index(string sortOrder, string currentFilter, string searchString, string currentFilter2, string searchString2,string currentFilter3, string searchString3, int? page)
{
  ViewBag.CurrentSort = sortOrder;
  ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";

  if (searchString != null)
  {
    page = 1;
  }
  else
  {
    searchString = currentFilter;
  }

ViewBag.CurrentFilter = searchString;

if (searchString2 != null)
  {
    page = 1;
  }
  else
  {
    searchString2 = currentFilter2;
  }

ViewBag.CurrentFilter2 = searchString2;

   if (searchString3 != null)
  {
    page = 1;
  }
  else
  {
    searchString3 = currentFilter3;
  }

ViewBag.CurrentFilter3 = searchString3;

var students = from s in db.Students orderby s.LastName
               select s;

if (!String.IsNullOrEmpty(searchString))
{
    //students = students.Where(s => s.LastName.Contains(searchString)
    //                       || s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
    case "name_desc":
        students = students.OrderByDescending(s => s.LastName);
        break;
    case "Date":
        students = students.OrderBy(s => s.EnrollmentDate);
        break;
    case "date_desc":
        students = students.OrderByDescending(s => s.EnrollmentDate);
        break;
    default:  // Name ascending 
        students = students.OrderBy(s => s.LastName);
        break;
}

 int pageSize = 3;
 int pageNumber = (page ?? 1);
 return View(students.ToPagedList(pageNumber, pageSize));
}

Stephen Muecke has mentioned very correctly in the comment

Irf
  • 4,285
  • 3
  • 36
  • 49