My application have page with few search boxes. When user clicks on respective search button a result grid is expected to load. That grid have few editable controls in which user can modifications and hit save button if needed to save the data.
We have implemented the functionality using AjaxForm and partial view.
have tried lot of tricks but was unsuccessful. Any working example demonstrated anywhere or suggestion to make this working ? There are lot of examples on web where usage of AjaxForm to load data is explained but didn’t found any for multiple (or nested?)
Thanks in advance.
Edit - Feb 24
Here is the sample I created using Visual Studio default MVC template, which is similar to the actual criteria explained above and have the same problem on Submit of Partial page ..
Views:
Index.cshtml
@using MvcApplication1.Models
@model SearchModel
@{
ViewBag.Title = "Home Page";
}
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" ></script>
<h2>@ViewBag.Message</h2>
<p>
To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
</p>
@Ajax.BeginForm("Search", "Home", new AjaxOptions { HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,UpdateTargetId = "SearchResults"})
{
<table>
<tr>
<td>
First Name:
</td>
<td>
@Html.TextBoxFor(m => m.SearchString)
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
<div id="SearchResults" style="color: Green;"></div>
Partial View - _SearchResult.cshtml
@using MvcApplication1.Models
@model MvcApplication1.Models.SearchModel
@{
ViewBag.Title = "Result Partial";
}
<h2>
testPartial</h2>
@Ajax.BeginForm("SearchResult", "Home", new AjaxOptions
{
HttpMethod = "Post"
})
{
<table>
@foreach (ResultModel item in Model.Result)
{
<tr>
<td>
Name:
</td>
<td>
@Html.DisplayFor(m => item.Name)
</td>
</tr>
<tr>
<td>
Address:
</td>
<td>
@Html.TextAreaFor(m => item.Address)
</td>
</tr>
}
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Models:
SearchModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplication1.Models
{
public class SearchModel
{
public string SearchString { get; set; }
public List<ResultModel> Result { get; set; }
}
}
ResultModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplication1.Models
{
public class ResultModel
{
public string Name { get; set; }
public string Address { get; set; }
}
}
Controller:
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
[HttpPost]
public ActionResult Search(SearchModel model)
{
//dummy search for result
List<ResultModel> result = new List<ResultModel>();
ResultModel res1 = new ResultModel();
res1.Name = model.SearchString + " 1";
res1.Address = "Dummy address";
result.Add(res1);
ResultModel res2 = new ResultModel();
res2.Name = model.SearchString + " 2";
res2.Address = "Rummy address";
result.Add(res2);
//assign seach results to model
model.Result = result;
return PartialView("_SearchResult", model);
}
[HttpPost]
public ActionResult SearchResult(SearchModel model)
{
//do something with results
List<ResultModel> res = model.Result; // null here !!
return RedirectToAction("Index");
}
}
}
SO, above sample will
Hope, this explain my problem.
Now that you have shown your actual code with a complete example your question can be answered.
The reason you are getting null
is because you are not respecting the standard naming convention for your input fields that the default model binder expects. Please read the following article
from Phil Haack to familiarize yourself with those conventions.
The problem with your code is the fact that you used a foreach
loop inside your partial to render the results instead of using an editor template. So replace your code inside _SearchResult.cshtml
with the following:
@using MvcApplication1.Models
@model SearchModel
@{
ViewBag.Title = "Result Partial";
}
<h2>testPartial</h2>
@using(Ajax.BeginForm("SearchResult", "Home", new AjaxOptions()))
{
<table>
@Html.EditorFor(x => x.Result)
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
and then define a custom editor template for the ResultModel
type (~/Views/Shared/EditorTemplates/ResultModel.cshtml
- warning, the name and location of your editor template is important because it works by convention):
@using MvcApplication1.Models
@model ResultModel
<tr>
<td>
Name:
</td>
<td>
@Html.DisplayFor(m => m.Name)
@Html.HiddenFor(m => m.Name)
</td>
</tr>
<tr>
<td>
Address:
</td>
<td>
@Html.TextAreaFor(m => m.Address)
</td>
</tr>
Things to notice:
Ajax.BeginForm
helper in a using
statement. You should do the same inside your Index.cshtml
view@Html.HiddenFor(m => m.Name)
) in order to send this value to the server when the form is submitted because you only had a TextArea for the Address field meaning that the Name would never have been sent to your server.