After many trial errors (mostly an error!), The answer was to not use the view model field for the search.
Here's the new search box in the main view:
@{ string searchName = ViewBag.SearchName; } @Html.EditorFor(x => searchName, new {htmlAttributes = new {@class = "form-control"}})
Then in action this change gets the value of searchName :
[HttpPost] [ValidateAntiForgeryToken] public PartialViewResult CreateSearch(string searchName, int? page) { if (string.IsNullOrEmpty(searchName)) return null; int number; var list = int.TryParse(searchName, out number) ? CustomerDataService.SearchCustomerByNumber(number) : CustomerDataService.SearchCustomerByName(searchName); var permitsVm = new PermitsViewModel {SearchVm = {Customers = list.ToPagedList(page ?? 1, 20)}}; ViewBag.SearchName = searchName; return PartialView("_PermitsCreateCustomerList", permitsVm); }
Note the ViewBag.SearchName ; which will be used to pass the value of the search field to a partial view.
<div id="contentPager"> @Html.PagedListPager(Model, page => Url.Action("SearchCustomers", "Permits", new { ViewBag.SearchName, page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing( new AjaxOptions() { HttpMethod = "POST", UpdateTargetId = "targetElement", OnSuccess = "onAjaxSuccess", OnFailure = "onAjaxFailure" })) </div>
In the above paging mechanism, we use the ViewBag to pass the search value back to the controller.
Update 1: You will also need the following on the main view (the one that contains the partial) to ensure that the anti-fake token will be sent when you click the numbers in the page:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) { if (options.type.toUpperCase() === "POST") { // We need to add the verificationToken to all POSTs var token = $("input[name^=__RequestVerificationToken]").first(); if (!token.length) return; var tokenName = token.attr("name"); // If the data is JSON, then we need to put the token in the QueryString: if (options.contentType.indexOf('application/json') === 0) { // Add the token to the URL, because we can't add it to the JSON data: options.url += ((options.url.indexOf("?") === -1) ? "?" : "&") + token.serialize(); } else if (typeof options.data === 'string' && options.data.indexOf(tokenName) === -1) { // Append to the data string: options.data += (options.data ? "&" : "") + token.serialize(); } } });
From here: https://gist.github.com/scottrippey/3428114
Update 2: You can use the view model on the controller, but must go through RouteValueDictionary :
<div id="contentPager"> @Html.PagedListPager(Model, page => Url.Action("SearchCustomers", "Permits", new RouteValueDictionary() { { "Page", page}, { "SearchVm.SearchCustomerNameNumber", Model.SearchVm.SearchCustomerNameNumber } }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing( new AjaxOptions() { HttpMethod = "POST", UpdateTargetId = "targetElement", OnSuccess = "onAjaxSuccess", OnFailure = "onAjaxFailure" })) </div>
In doing so, you change the action:
[HttpPost] [ValidateAntiForgeryToken] public PartialViewResult CreateSearch(PermitsViewModel permitsVm) { if (string.IsNullOrEmpty(permitsVm.SearchVm.SearchCustomerNameNumber)) return null; int number; var list = int.TryParse(permitsVm.SearchVm.SearchCustomerNameNumber, out number) ? CustomerDataService.SearchCustomerByNumber(number) : CustomerDataService.SearchCustomerByName(permitsVm.SearchVm.SearchCustomerNameNumber); permitsVm.SearchVm.Customers = list.ToPagedList(permitsVm.Page ?? 1, 10); return PartialView("_PermitsCreateCustomerList", permitsVm); }
The complex help objects on RouteValueDictionary came from here: fooobar.com/questions/494245 / ...