Is there a tutorial or code example of using Ajax.BeginForm
within Asp.net MVC 3 where unobtrusive validation and Ajax exist?
This is an elusive topic for MVC 3, and I cannot seem to get my form to work properly. It will do an Ajax submit but ignores the validation errors.
Example:
Model:
public class MyViewModel
{
[Required]
public string Foo { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return Content("Thanks", "text/html");
}
}
View:
@model AppName.Models.MyViewModel
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<div id="result"></div>
@using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "result" }))
{
@Html.EditorFor(x => x.Foo)
@Html.ValidationMessageFor(x => x.Foo)
<input type="submit" value="OK" />
}
and here's a better (in my perspective) example:
View:
@model AppName.Models.MyViewModel
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/index.js")" type="text/javascript"></script>
<div id="result"></div>
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Foo)
@Html.ValidationMessageFor(x => x.Foo)
<input type="submit" value="OK" />
}
index.js
:
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$('#result').html(result);
}
});
}
return false;
});
});
which can be further enhanced with the jQuery form plugin.
I think that all the answers missed a crucial point:
If you use the Ajax form so that it needs to update itself (and NOT another div outside of the form) then you need to put the containing div OUTSIDE of the form. For example:
<div id="target">
@using (Ajax.BeginForm("MyAction", "MyController",
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "target"
}))
{
<!-- whatever -->
}
</div>
Otherwise you will end like @David where the result is displayed in a new page.
UnobtrusiveJavaScriptEnabled
to true anywhere
I got Darin's solution working eventually but made a few mistakes first which resulted in a problem similar to David (in the comments below Darin's solution) where the result was posting to a new page.
Because I had to do something with the form after the method returned, I stored it for later use:
var form = $(this);
However, this variable did not have the "action" or "method" properties which are used in the ajax call.
$(document).on("submit", "form", function (event) {
var form = $(this);
if (form.valid()) {
$.ajax({
url: form.action, // Not available to 'form' variable
type: form.method, // Not available to 'form' variable
data: form.serialize(),
success: function (html) {
// Do something with the returned html.
}
});
}
event.preventDefault();
});
Instead you need to use the "this" variable:
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (html) {
// Do something with the returned html.
}
});
jQuery
object with form as the selector. form[0]
would have the properties. It's also good practise to prefix any jQuery
variables with $
to more easily identify them.
Darin Dimitrov's solution worked for me with one exception. When I submitted the partial view with (intentional) validation errors, I ended up with duplicate forms being returned in the dialog:
https://i.stack.imgur.com/2Tddt.png
To fix this I had to wrap the Html.BeginForm in a div:
<div id="myForm">
@using (Html.BeginForm("CreateDialog", "SupportClass1", FormMethod.Post, new { @class = "form-horizontal" }))
{
//form contents
}
</div>
When the form was submitted, I cleared the div in the success function and output the validated form:
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$('#myForm').html('');
$('#result').html(result);
}
});
}
return false;
});
});
Partial Views
to render the create function below the index page. I can get all the validation messages in the partial View. But when the Create
is successful the index is displayed twice. I have no Html.BeginForm
in my Index View.
UpdateTargetId = "myForm"
instead
If no data validation excuted, or the content is always returned in a new window, make sure these 3 lines are at the top of the view:
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
Example
//In Model
public class MyModel
{
[Required]
public string Name{ get; set; }
}
//In PartailView //PartailView.cshtml
@model MyModel
<div>
<div>
@Html.LabelFor(model=>model.Name)
</div>
<div>
@Html.EditorFor(model=>model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
</div>
In Index.cshtml view
@model MyModel
<div id="targetId">
@{Html.RenderPartial("PartialView",Model)}
</div>
@using(Ajax.BeginForm("AddName", new AjaxOptions { UpdateTargetId = "targetId", HttpMethod = "Post" }))
{
<div>
<input type="submit" value="Add Unit" />
</div>
}
In Controller
public ActionResult Index()
{
return View(new MyModel());
}
public string AddName(MyModel model)
{
string HtmlString = RenderPartialViewToString("PartailView",model);
return HtmlString;
}
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
you must be pass ViewName and Model to RenderPartialViewToString method. it will return you view with validation which are you applied in model and append the content in "targetId" div in Index.cshtml. I this way by catching RenderHtml of partial view you can apply validation.
Ajax forms work asynchronously using Javascript. So it is required, to load the script files for execution. Even though it's a small performance compromise, the execution happens without postback.
We need to understand the difference between the behaviours of both Html and Ajax forms.
Ajax:
Won't redirect the form, even you do a RedirectAction(). Will perform save, update and any modification operations asynchronously.
Html:
Will redirect the form. Will perform operations both Synchronously and Asynchronously (With some extra code and care).
Demonstrated the differences with a POC in below link. Link
Prior to adding the Ajax.BeginForm. Add below scripts to your project in the order mentioned,
jquery-1.7.1.min.js jquery.unobtrusive-ajax.min.js
Only these two are enough for performing Ajax operation.
Success story sharing