I've got a simple example here. Basically a form which when submitted will reload itself via an ajax request. The problem is when this happens, the unobtrusive javascript no longer works. I assume I could add the validate and unobtrusive files in the html i get back from the ajax call, but there must be an easier way to re-wire the validators, no?
Notice I'm hijacking my submit button in order to do an AJAX request which will replace the form in the dom, from the html which is returned from the ajax request.
Model:
public class Foo
{
public int Bar { get; set; }
}
Controller:
public class FooController : Controller
{
public ActionResult Index()
{
return View(new Foo{});
}
[HttpPost]
public ActionResult Form(Foo model)
{
return View(model);
}
[HttpPost]
public ActionResult Index(Foo model)
{
return View();
}
}
Index.cshtml
#model PartialPostBackValidation.Models.Foo
#{
ViewBag.Title = "Index";
}
<h2>#Html.ActionLink("Index", "Index")</h2>
<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>
$(function () {
$("body").on("click", ".ajax-submit", function () {
var form = $(this).parents("form");
$.post(
form.attr("action"),
form.serialize(),
function (html) {
form.replaceWith(html);
}
);
return false;
});
});
</script>
#{Html.RenderPartial("Form");}
Form.cshtml
#model PartialPostBackValidation.Models.Foo
#{
ViewBag.Title = "Index";
Layout = null;
}
#using (Html.BeginForm("Form", "Foo")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Foo</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Bar)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Bar)
#Html.ValidationMessageFor(model => model.Bar)
</div>
<p>
<input type="submit" value="Create" class="ajax-submit" />
</p>
</fieldset>
}
To get the validation to work you simply have to re-enable it on the form once content is loaded dynamically:
$('#form-id').removeData('validator');
$('#form-id').removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse('#form-id'); <<<<<< Just having this could be enough but some people complain that without removingData first it doesn’t always work.
p.s. Ofcourse you're going to need to add an id attribute to your #using (Html.BeginForm("Form", "Foo"))
Related
I am changing a create form to become a modal dialog and jquery Unobtrusive validation stops working and don't know how to fix it.
Index.cshtml has a link to trigger a modal dialog.
Create
#section scripts{
<script type="text/javascript">
$('#createCustomer').on('click', function () {
$.get('/Customer/Create', function (data) {
$('#modalContainer').html(data);
$('#myModal').modal({});
});
});
Create.cshtml is a partial view.
#model Demo.Web.Models.CustomerVm
#using (Html.BeginForm("Create", "Customer", FormMethod.Post, new { #id="createForm" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Customer</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
On the controller there is an actionmethod which returns a partial view for create form.
public ActionResult Create()
{
return PartialView("Create");
}
view modal
public class CustomerVm
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
before i changed it to be a modal dialog .. everything was working but now i don't know how to fix it. How can i make validation work with dialog? Obviously, I don't want to rewrite validation rules on client script .. i want to get it working from view model .. thanks.
Because the form is not added to the page when the page loads, the unobtrusive validation will not pick it up. There are two ways to fix this.
Include the form on the page during the initial load. This will cause the form to be recognized and validation will occur. You can throw the partial view in a hidden div. Then your JavaScript will just show the modal dialog.
Manually register the form with the unobtrusive validation after adding it to the page. Something like $.validator.unobtrusive.parse("#id-of-the-form");
If you are loading the dialog dynamically just register the unobtrusive validation in the containing element's change event:
$('#modal-container').change(
function() {
$.validator.unobtrusive.parse("#your-form-id");
});
In partialview of create page -> modal-header, model-body, modal-footer and javascript code in the <script>your code </script> - don't put <script>your code</script> in #section Scripts{} and it will work.
Just add the following scripts in your Create form 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>
Adding a new comment to share a more modern solution:
Use BundleConfig.cs in the App_Start folder of your MVC project.
namespace MySolution
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/Site.min.css",
"~/Content/bootstrap.min.css"));
bundles.Add(new ScriptBundle("~/Scripts/js").Include(
"~/Scripts/jquery-3.3.1.min.js",
"~/Scripts/jquery.validate.min.js",
"~/Scripts/jquery.validate.unobtrusive.min.js"));
}
}
}
I use jquery validation in my MVC application and after selecting and leaving an input, it is automatically validated. On the other hand, if user does not select an input, they are not validated even if submitting the form. So, I want to validate all the related fields after pressing submit button on MVC Razor page. How can I do this?
I created a simple example. It's just to give you a start, you still have to modify this code to your needs. This example is using jquery form validation.
Model
public class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
Controller & Action
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
View
#model JqueryValidationDemo.Models.Vehicle
#{
ViewBag.Title = "Jquery Form Validation Demo";
}
<script src="~/Scripts/jquery-2.0.3.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script>
$(document).ready(function () {
$('#myform').validate({ // initialize the plugin
rules: {
Make: {
required: true,
minlength: 2
},
Model: { required: true }
},
messages: {
Make: {
required: "Make is required!",
minlength: "Make Name at least 2 characters long!"
},
Model: {
required: "Modl is required!"
}
},
submitHandler:
$("#myform").on('submit', function () {
if ($("#myform").valid()) {
//Do something here
alert("Validation passed");
}
return false;
})
})
});
</script>
#using (Html.BeginForm("Index", "Home", FormMethod.Get, new { #id = "myform" }))
{
<fieldset>
<legend>Vehicle</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Make)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Make)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Model)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Model)
</div>
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
}
Note: Be careful with your jquery reference, you don't need to use jquery.validate.unobtrusive.js.
Hope it will give you a good start.
Update:
Remove keyup or keypress
Remove keypress
$("#youElementId").bind('keypress', function (e) {
if (e.keyCode == 13) {
return false;
}
return true;
});
I have Area in MVC3 as mentioned below.
Model
public class AdminModule
{
[Display(Name = "MyName")]
[Required(ErrorMessage = "MyName is missing")]
public String MyName { get; set; }
}
I have Partial View with following code.
#model _1.Areas.Admin.Models.AdminModule
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString">
</p>
<input type="submit" value="Click here" id="btn" />
}
View
#model _1.Areas.Admin.Models.AdminModule
#{
ViewBag.Title = "Index";
Layout = "~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml";
}
<h2>
Index</h2>
<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript">
</script>
<script type="text/javascript" src="/scripts/jquery.unobtrusive-ajax.js">
</script>
<div id="myForm">
#Html.Partial("_PartialPage1", Model)
</div>
Layout
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div>
#RenderBody()
</div>
</body>
</html>
Controller Actions
[HttpPost]
public ActionResult Index(AdminModule model)
{
return PartialView(model);
}
[HttpGet]
public ActionResult Index()
{
AdminModule model = new AdminModule();
model.MyName = "My Name";
return View(model);
}
Confusion
When I submit first time.
I get output like below
and form show like this. Question is - Why is index word coming two times?
When I click second time, form appearance remains same and output shows like below.
Question - Why is Jquery coming so many times ?
You could use an Ajax.BeginForm instead of a regular form. But first you should decide which section of your page you want to be updated after the AJAX call.
Let's suppose the following scenario: if the AJAX call is successful you want to update some section of your DOM with some result and if the AJAX fails you want to update the form and display the error message instead.
To implement this you could start by placing the form inside a partial (_MyForm.cshtml):
#model _1.Models.HomeModels
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<input type="submit" value="Click here" id="btn" />
}
#if (Model.SomeResultProperty != null)
{
<div>#Model.SomeResultProperty</div>
}
and then you could have your main view reference this partial:
#model _1.Models.HomeModels
<div id="myForm">
#Html.Partial("_MyForm", Model)
</div>
The next step is to update your controller action that will handle the AJAX call:
[HttpPost]
public ActionResult Index(HomeModels model)
{
if (ModelState.IsValid)
{
// validation succeeded => we could set the result property
// on the model to be displayed:
model.SomeResultProperty = "this is the result";
}
return PartialView("_MyForm", model);
}
and finally you need to include the jquery.unobtrusive-ajax.js script to your page in order for the Ajax.BeginForm helper to work:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.unobtrusive-ajax.js")"></script>
Use Ajax.BeginForm instead.
Did you reference validation scripts in your page?
I am interested in creating an AJAX form submit in a jQuery overlay. I am not sure how to approach this, do I just toss a partial view into the overlay?
I want to pass to the server the data in the form of a model so I can save it the the data base, I need to be able to create some sort of indication as to whether or not the request succeed. Can anyone guide me through this?
I am kinda new with AJax.
You could use jQuery UI Dialog. For example let's suppose that you have a view model:
public class MyViewModel
{
public string Foo { get; set; }
[Required]
public string Bar { get; set; }
}
and a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Modal()
{
return PartialView(new MyViewModel());
}
[HttpPost]
public ActionResult Modal(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
return Json(new { success = true });
}
}
In this example the Index action will serve the main view which will simply contain a link allowing to show the form as a modal dialog.
Here's the Index.cshtml view:
#Ajax.ActionLink(
"show form in modal",
"modal",
new AjaxOptions { OnSuccess = "onModalLoad" }
)
<div id="modal"></div>
and the Modal.cshtml partial which will contain the form:
#model MyViewModel
#using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "onSubmitSuccess" }))
{
<div>
#Html.LabelFor(x => x.Foo)
#Html.EditorFor(x => x.Foo)
#Html.ValidationMessageFor(x => x.Foo)
</div>
<div>
#Html.LabelFor(x => x.Bar)
#Html.EditorFor(x => x.Bar)
#Html.ValidationMessageFor(x => x.Bar)
</div>
<button type="submit">OK</button>
}
The last step is to wire everything using javascript. Here are the 2 callbacks used:
var onModalLoad = function (result) {
$('#modal').html(result).dialog();
}
var onSubmitSuccess = function (result) {
if (!result.success) {
$('#modal').html(result);
} else {
alert('thanks for submitting');
$('#modal').dialog('close');
}
};
and that's it.
Don't forget to reference the jquery-ui and jquery.unobtrusive-ajax scripts to your page:
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
I am just getting started with ASP.Net MVC 3 with the Facebook C# SDK. I've had pretty good luck so far, but I am having an issue when returning RedirectToAction or CanvasRedirectToAction in a Controller method.
When I click either the Submit or Create New_ button in the view below, I always get send to
/app/app/Event/Create instead of app/Event/Create, resulting in a 404
Thanks in advance!
EventController:
[CanvasAuthorize(Permissions = "user_about_me,user_events,create_event")]
public ActionResult Index()
{
return View();
}
[CanvasAuthorize(Permissions = "user_about_me,user_events,create_event")]
public ActionResult Create()
{
return View();
}
[CanvasAuthorize(Permissions = "user_about_me,user_events,create_event")]
public ActionResult Persist()
{
return this.CanvasRedirectToAction("Index", "Event");
}
Event/Create.cshtml
#using Facebook.Web.Mvc
#model App.Models.Event
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<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>
#using (Html.BeginForm("Persist", "Event")) {
#Html.FacebookSignedRequest();
#Html.ValidationSummary(true)
<fieldset>
<legend>Event</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Create" />
#Html.CanvasActionLink("Create New", "Persist")
</p>
</fieldset>
}
It doesn't appear like it's easily possible. My guess is that the javascript that looks at the URL in the frame within the C# SDK doesn't do a good job of creating the correct path to redirect to. I just got rid of the virtual directory to solve the problem for now.