ASP.NET MVC - prevent submit of invalid form using jQuery unobtrusive validation - asp.net

I have an ASP.NET project that automatically wires up client side validation using jQuery.Validate and the unobtrusive wrapper built by ASP.NET.
a) I definitely have the appropriate libraries: jquery.js, jquery.validate.js, & jquery.validate.unobtrusive.js
b) And the MVC rendering engine is definitely turned on (ClientValidationEnabled & UnobtrusiveJavaScriptEnabled in the appSettings section of the web.config)
Here's a trivial example where things are broken:
Model:
public class Person
{
[Required]
public string Name { get; set; }
}
Controller:
public ActionResult Edit()
{
Person p = new Person();
return View(p);
}
View:
#model validation.Models.Person
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
}
This generates the following client side markup:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.15.1/jquery.validate.js"></script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.validate.unobtrusive.js"></script>
<form action="/Person" method="post">
<div class="validation-summary-valid" data-valmsg-summary="true">
<ul><li style="display:none"></li></ul>
</div>
<label for="Name">Name</label>
<input data-val="true" data-val-required="The Name field is required." id="Name" name="Name" type="text" value="" />
<input type="submit" value="Save" />
</form>
When run it will perform the client side validation, noting that some form elements are invalid, but then also post back to the server.
Why is it not preventing postback on a form with an invalid state?

The Problem
It turns out this happens when you don't include a #Html.ValidationMessageFor placeholder for a given form element.
Here's a deeper dive into where the problem occurs:
When a form submits, jquery.validate.js will call the following methods:
validate: function( options ) {
form: function() {
showErrors: function(errors) {
defaultShowErrors: function() {
showLabel: function(element, message) {
this.settings.errorPlacement(label, $(element) )
Where errorPlacement will call this method in jquery.validate.unobtrusive.js:
function onError(error, inputElement) {
var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false;
When we don't add a placeholder for the validation message, $(this).find(...) won't find anything.
Meaning container.attr("data-valmsg-replace") will return undefined
This poses a problem is when we try to call $.parseJSON on an undefined value. If an error is thrown (and not caught), JavaScript will stop dead in its tracks and never reach the final line of code in the original method (return false) which prevents the form from submitting.
The Solution
Upgrade jQuery Validate Unobtrusive
Newer versions of jQuery Validate handle this better and check for nulls before passing them to $.parseJSON
function onError(error, inputElement) { // 'this' is the form element
var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
replaceAttrValue = container.attr("data-valmsg-replace"),
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;
Add ValidationMessageFor
To address the core problem, for every input on your form, make sure to include:
#Html.ValidationMessageFor(model => model.Name)
Which will render the following client side markup
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.15.1/jquery.validate.js"></script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.validate.unobtrusive.js"></script>
<form action="/Person" method="post">
<div class="validation-summary-valid" data-valmsg-summary="true">
<ul><li style="display:none"></li></ul>
</div>
<label for="Name">Name</label>
<input data-val="true" data-val-required="The Name field is required." id="Name" name="Name" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
<input type="submit" value="Save" />
</form>

Related

Multiple submit button fail to find the handler method in asp.net core 3.1 razor page application

I have a single form which has two button one to upload image using ajax call & other is main submit button which saves all the data.
I am still very new to asp.net core and trying different thing to learn asp.net core with razor page.
I read lot of article about multiple form submit but most of then where using two form with submit button for each.
My issue is when i hit the submit button it fails to find the handler method, after full days troubleshoot nothing worked and last few article point to error/failure to antiforgery, i not sure how to implement it in below code as i tried few but they gave error may be article where old referencing core 2.2 etc example1 example2
I am not sure about the what exactly is causing the issue any help would be appreciate.
I am trying to upload Image using Ajax method in asp.net core Razor pages, I am main form in will all input fields are kept and with the form for Fileupload i am also added addition button which is for file upload using Ajax, When i hit the
<input type="submit" value="Upload Image" asp-page-handler="OnPostUploadImage" id="btnUploadImage" />
i want it to call OnPostUploadImage method in pageModel file but it alway goes to default OnPost method. when i rename the OnPost to OnPost2 nothing happend..
How can i call OnPostUploadImage() on button btnUploadImage click event.
When i hit click btnUploadImage it generates following error on browser console
Error in FF
XML Parsing Error: no root element found Location:
https://localhost:44364/Admin/News/NewsCreate?handler=OnPostUploadImage
Line Number 1, Column 1:
Error in Chrome
jquery.min.js:2 POST
https://localhost:44364/Admin/News/NewsCreateMultipleSubmit?handler=OnPostUpLoadImage
400 (Bad Request)
event though path looks fine but it cant find it as per error message
#page
#model BookListRazor.Pages.Admin.News.NewsCreateModel
#{
ViewData["Title"] = "News Create";
Layout = "~/Pages/Shared/_LayoutAdmin.cshtml";
}
<div class="border container" style="padding:30px;">
<form method="post" enctype="multipart/form-data">
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
<input hidden asp-for="News.NewsImage" />
<input id="fileName" hidden value="" />
<div class="form-group row">
<div class="col-2">
<label asp-for="News.NewsHeading"></label>
</div>
<div class="col-10">
<input asp-for="News.NewsHeading" class="form-control" />
</div>
<span asp-validation-for="News.NewsHeading" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="News.NewsImage"></label>
</div>
<div class="col-10">
#*<input asp-for="News.NewsImage" type="file" class="form-control" id="NewsImage">*#
#*Photo property type is IFormFile, so ASP.NET Core automatically creates a FileUpload control *#
<div class="custom-file">
<input asp-for="NewsImageForUpload" class="custom-file-input form-control">
<label class="custom-file-label">Click here to change photo</label>
<input type="submit" value="Upload Image" asp-page-handler="OnPostUploadImage" id="btnUploadImage" />
</div>
</div>
<span id="imageStatus" class="text-danger"></span>
<span asp-validation-for="NewsImageForUpload" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-3 offset-3">
<input id="btnSave" type="submit" value="Create" class="btn btn-primary form-control" />
</div>
<div class="col-3">
<a asp-page="Index" class="btn btn-success form-control">Back to List</a>
</div>
</div>
</form>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.ckeditor.com/4.14.0/full/ckeditor.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script>
$(document).ready(function () {
$("#btnSave").addClass("disable-button");
$('.custom-file-input').on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).next('.custom-file-label').html(fileName);
$("#fileName").val(fileName);
$("#btnSave").removeClass("disable-button");
});
if ($("#fileName").val() == "") {
//alert("Select Image...");;
}
});
</script>
</div>
#section Scripts{
<partial name="_ValidationScriptsPartial" />
<script>
$(function () {
$('#btnUploadImage').on('click', function (evt) {
console.log("btnUploadImage");
evt.preventDefault();
console.log("btnUploadImage after evt.preventDefault()");
$.ajax({
url: '#Url.Page("", "OnPostUploadImage")',
//data: new FormData(document.forms[0]),
contentType: false,
processData: false,
type: 'post',
success: function () {
alert('Uploaded by jQuery');
}
});
});
});
</script>
}
.cs file CODE
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
return Page();
}
else
{
return Page();
}
}
public IActionResult OnPostUploadImage()
{
//Some code here
}
Verify that you add the following code to the ConfigureServices method of startup.cs:
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
If you want to enter the OnPostUploadImage method, the url of the Ajax request needs to be changed to #Url.Page("", "UploadImage") without adding OnPost.
And the Ajax request should send the anti-forgery token in request header to the server.
Change your ajax as follow:
#section Scripts{
<partial name="_ValidationScriptsPartial" />
<script>
$(function () {
$('#btnUploadImage').on('click', function (evt) {
console.log("btnUploadImage");
evt.preventDefault();
console.log("btnUploadImage after evt.preventDefault()");
$.ajax({
url: '#Url.Page("", "UploadImage")',
//data: new FormData(document.forms[0]),
contentType: false,
processData: false,
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
type: 'post',
success: function () {
alert('Uploaded by jQuery');
}
});
});
});
</script>
}
You can refer to this for more details.

Form action called before ajax request

I'm trying to validate some data using javascript, so after created this form:
<form asp-controller="User" asp-action="UpdateUser" asp-antiforgery="true" id="userInformations">
<div class="form-group">
<label class="col-lg-6 control-label">#Localizer["OldPassword"] (*)</label>
<div class="col-lg-12">
<input class="form-control" required id="oldPassword"
asp-for="#Model.ExistingPassword" type="password" />
</div>
<div class="form-group">
<label class="col-lg-6 control-label">#Localizer["NewPassword"] (*)</label>
<div class="col-lg-12">
<input class="form-control" required id="newPassword"
asp-for="#Model.Password" type="password" />
</div>
</div>
<button type="submit" class="btn btn-primary">Store</button>
</form>
I binded a javascript function that intercept the submit:
$('#userInformations').on('submit', function (event) {
event.preventDefault();
//validate some fields
//execute ajax request
$.ajax({
url: $(this).attr('action'),
type: "POST",
data: $(this).serialize(),
success: function (result) {
alert(true);
console.log(result)
},
error: function (data) {
console.log(data);
}
});
});
Now when I press the submit button, the method UpdateUser in the UserController is called first of the javascript function, and I don't understand why happen this because I used preventDefault.
How can I prevent to call the asp net action binded to the form?
The effect you want is to use javascript to validate some data , and implement the action call through ajax when you press the submit button?
I use the code you provided and it works.
Try the following two ways:
1.Add the following piece of code above your javascript
<script src="~/lib/jquery/dist/jquery.js"></script>
2.Or directly write your javascript in #section Scripts { }
If you want JavaScript first than you have to remove asp-Controller and action from form, than you can validate form by JavaScript and send data through Ajax call to Controller/action.

ASP.NET MVC 5 Form Validation and Error Handling

Trying to implement data validation and error handling on a simple contact form. When I add the check for ModelState.IsValid I'm in a chicken and egg situation. I have looked at other similar questions and am just not getting this. Moving from Web Forms to MVC and struggling. Trying to toggle HTML elements based on what's happening - success/error message, etc. RIght now, not even the validation is working.
Right now I'm just trying to get server-side validation working but would welcome advice on how to add client-side validation also; for example, is it necessary to use jQuery for this or is there something baked in?
View:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
if (ViewData["Error"] == null && ViewData["Success"] == null)
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
<input type="text" name="email" placeholder="Email..."
class="contact-email" id="contact-email">
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
<input type="text" name="subject" placeholder="Subject..."
class="contact-subject" id="contact-subject">
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
<textarea name="message" placeholder="Message..."
class="contact-message" id="contact-message"></textarea>
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
else if (ViewData["Error"] == null && ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
else if (ViewData["Error"] != null)
{
<h3>Oops!</h3>
<p>
We apologize. We seem to be having some problems.
</p>
<p>
Please come back and try again later. Alternatively,
call us anytime at (xxx) xxx-xxxx.
</p>
}
}
Model:
public class ContactModel
{
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Subject is required")]
public string Subject { get; set; }
[Required(ErrorMessage = "Message is required")]
public string Message { get; set; }
}
Controller:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ViewData["Error"]
= "Something went wrong - please try again later.";
return View("Error");
}
}
return View();
}
Error View:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
UPDATE - 05/09/2017
Per Guruprasad's answer, if ModelState.IsValid evaluates to false, then no validation error messages are being reported on the form.
Note I had to change the AddModelError signature to not use the "Extension ex" parameter:ModelState.AddModelError("Error", "Server side error occurred"); as I do not want system errors being reported to users.
Note also that at this point I am only trying out validation on the server side (have yet to work through client-side validation).
I have updated the Contact.cshtml view as follows as no model errors were being displayed - I have included the Bootstrap .has-error and .help-block CSS rules for the validation errors:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group has-error">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email",
placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(model => model.Email, String.Empty,
new { #class="help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text",
name = "subject",
placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(model => model.Subject, String.Empty,
new { #class = "help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message",
placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(model => model.Message, String.Empty,
new { #class = "help-block" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to
call us at (xxx) xxx-xxxx at any time.
</p>
}
}
There are multiple things you need to understand here. Let me go point by point.
Its good to know that you have your model designed, but how your view gets to know that it has a model to bind for itself and when posting the form contents, how would server comes to know that, there is a model to be received. So on the first instance, you need to construct your view binding the model. To bind a model in a view, you need to first get a reference/declare it at the top, letting view know that, ok, here is a model for you to generate my view.
Well, you have ValidationSummary to true, then I would suggest that, instead of using ViewData to pass error message, you can use ModelState.AddModelError and let ValidationSummary take care of that. As a side note, you might also want to take care of this issue and you can resolve the same with answers mentioned in the same post. If you are not using or do not want to use Html.ValidationSummary, then you can stick to your current view.
Now, to display Success message, you can either use TempData or ViewData and follow the same structure as you have in your view now. Here is one more post to let you work on that.
Last and most important on View part is binding model properties to View elements. Use Razor View extension helpers to generate View for your model. You have #Html.TextBoxFor,#Html.TextAreaFor etc., You also have #Html.TextBox, #Html.TextArea which is not for binding model properties, but just to generate a plain HTML view. You can add other html properties within these helpers as shown in the updated view below. I would suggest to dig down more on the overloads available for these helpers.
So here is your updated view.
#model SOTestApplication.Models.ContactModel #*getting model reference*#
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#*Usage of helpers and html attributes*#
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
Controller Side validation
Not much to say on this part as it looks good. But based on few of my points above, I would suggest you to add ModelState.AddModelError instead of using ViewData for error messages. Eliminate your if conditions in view, so that contact form remains, even after postback. Now if you want to persist the values after server side validation, then just pass back the model to your view in your post method. Updated Controller would be:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ModelState.AddModelError("Server side error occurred", ex.Message.ToString());
}
}
return View(contactModel); //this will persist user entered data on validation failure
}
Client Side Validation
As far as this portion is considered, you have few more things to set up in your application.
You need to add Html.EnableClientValidation(true); and Html.EnableUnobtrusiveJavaScript(true); to your application. There are various possible ways to add this. You can add this on Web.config file under appSettings for global implication Or you can add this in particular view as mentioned in below updated View example.
Global Implication in Web.Config ex:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
If you have noticed your BundleConfig.cs file under App_Start directory, you would have seen below entries created by default. These are the jquery stuffs responsible for your Client Side validation.
jQuery and jQueryVal entries
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
Next Step is to add reference to these files/use #section Scripts to render these bundles either in _Layout.cshtml or in any specific view. When you include this in _Layout.cshtml. these scripts/bundles are rendered wherever you use this layout with other views. So basically, its your call on where to render these.
For example here, I would render these in Contact.cshtml view soon after adding reference to model.
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
One Last thing to make this work here is that you need to use #Html.ValidationMessageFor razor extension and let MVC do the binding of error messages on particular properties. Also for these error messages to be displayed in the View, you need to specify ErrorMessage for each property in your model as you are doing it now with Required(ErrorMessage=... for each properties in model. There are more to know about these stuffs if you explore it in detail.
Your updated view with proper validations added.
#model SOTestApplication.Models.ContactModel
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
#using (Html.BeginForm("Contact", "Contacts", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(m => m.Message)
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
}
Hope I have clarified most of your doubts with these points. Happy Coding.. :)

Play Framework How can i pass collection to action create()?

I am starter with Play Framework. I got a problem when i passed parameters.
I want to pass a collection from view to controller. And i do not know how to do this. I always get "null" when i get a collection from view.
My code below:
Code in controller:
public static void create(List<Book> books) throws Exception {
for(Book book : books){
System.out.println(book.get(0).author) // i got null :(
}
}
Code in HTML
Book 1:
<input type="text" name="books.author" />
<input type="text" name="books.title" />
Book 2:
<input type="text" name="books.author" />
<input type="text" name="books.title" />
When i submit, i want to add 2 records into database include Book1 and Book2. Please support me
Thanks
You can make this work by simplying add the array indicator to your HTML code
Book 1:
<input type="text" name="books[0].author" />
<input type="text" name="books[0].title" />
Book 2:
<input type="text" name="books[1].author" />
<input type="text" name="books[1].title" />
I have tested this solution, and it works fine.
Also note that your println will not compile, as you are calling get(0) on the Book object, and not the List object. If you just println book.author, it outputs the author as required.
In case anyone needs an example of the Javascript for dyanmically adding and removing books (JQUERY needed):
<script type="text/javascript">
$(document).ready(function() {
var bookCount=0;
$('#btnAddBook').click(function() {
bookCount++;
//newElem = go up a to the parent div then grab the previous container
var newElem = $(this).parent().prev().clone().attr('id', 'book[' + bookCount + ']');
//for each input inside the div, change the index to the latest bookCount
$(newElem).find("input").each(function(){
var name = $(this).attr('name');
var leftBracket = name.indexOf("[");
var rightBracket = name.indexOf("]");
var beforeBracketString = name.substring(0,leftBracket+1);//+1 to include the bracket
var afterBracketString = name.substring(rightBracket);
$(this).attr('name', beforeBracketString + bookCount + afterBracketString);
});
//insert it at the end of the books
$(this).parent().prev().after(newElem);
$(newElem).find("input").each(function(){
$(this).attr('id', $(this).attr('id') + bookCount);
});
//enable the remove button
$('#btnRemovebook').removeAttr('disabled');
//If we are at 16 divs, disable the add button
if (bookCount == 15)
$(this).attr('disabled','disabled');
});
$('#btnRemoveBook').click(function() {
bookCount--;
//remove the last book div
$(this).parent().prev().remove();
//in case add was disabled, enable it
$('#btnAddbook').removeAttr('disabled');
//never let them remove the last book div
if (bookCount == 0)
$(this).attr('disabled','disabled');
});
});
</script>
<!-- HTML Snippet -->
<div id="book[0]">
<label> Book: </label>
<input type="text" name="books[0].author" value="Author" />
<input type="text" name="books[0].title" value="Title" />
</div>
<div>
<input type="button" id="btnAddbook" value="Add another book" />
<input type="button" id="btnRemovebook" value="Remove last book" disabled="disabled" />
</div>
<!-- REST of the HTML -->

ajax post of form and validation

Does ajax post supports the usual validation upon submit of a form?
Is
#using(Html.BeginForm()){
#Hml.ValidationSummary()
<input type="submit" value ="Save">
}
same as
#using(Html.BeginForm(new {id=FormTest})){
#Hml.ValidationSummary()
<input type="button" value= Save>
}
<script type="javascript">
$("#Save").click(function(){
$("#FormTest").submit();
});
</script>
There are so many wrong things going on here that I really don't know where to start.
Your Html.BeginForm definition is wrong: you are confusing routeValues with htmlAttributes. See below for solution
Your button doesn't have an id so your javascript selector will most probably fail
All those efforts are not necessary because a simple <input type="submit"> button does it out of the box.
Now lets suppose that somehow you are a fan of javascript and you want to write it (I don't know why would you want to write code but anyway). So start by fixing your form definition:
#using(Html.BeginForm("someAction", "someController", FormMethod.Post, new { id = "FormTest" }))
{
#Html.ValidationSummary()
#Html.TextBoxFor(x => x.Foo)
<input type="button" value="Save" id="Save" />
}
and then you could do this:
$('#Save').click(function() {
var form = $('#FormTest');
if (form.valid()) {
form.submit();
}
});
obviously you must ensure that the client scripts are properly included on your page:
<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>
and of course the real solution I would recommend you is to use a submit button for submitting forms:
#using(Html.BeginForm("someAction", "someController", FormMethod.Post, new { id = "FormTest" }))
{
#Html.ValidationSummary()
#Html.TextBoxFor(x => x.Foo)
<input type="submit" value="Save" id="Save" />
}
Now you don't need any javascript (obviously you still need the two script inclusions for the unobtrusive validation).

Resources