126

I have simple ASP.NET MVC action like this :

public ActionResult Edit(EditPostViewModel data) { } 

The EditPostViewModel have validation attributes like this :

[Display(Name = "...", Description = "...")] [StringLength(100, MinimumLength = 3, ErrorMessage = "...")] [Required()] public string Title { get; set; } 

In the view I am using the following helpers :

 @Html.LabelFor(Model => Model.EditPostViewModel.Title, true) @Html.TextBoxFor(Model => Model.EditPostViewModel.Title, new { @class = "tb1", @Style = "width:400px;" }) 

If I do a submit on a form that this textbox is placed in a validation will be done first on client and then on service(ModelState.IsValid).

Now I got a couple of questions :

  1. Can this be used with jQuery ajax submit instead? What I am doing is simply remove the form and on clicking the submit button a javascript will gather data and then run the $.ajax.

  2. Will the server side ModelState.IsValid work?

  3. How can I forward validation problem back to the client and present it as if Im using the build int validation(@Html.ValidationSummary(true))?

Example of Ajax call :

function SendPost(actionPath) { $.ajax({ url: actionPath, type: 'POST', dataType: 'json', data: { Text: $('#EditPostViewModel_Text').val(), Title: $('#EditPostViewModel_Title').val() }, success: function (data) { alert('success'); }, error: function () { alert('error'); } }); } 

Edit 1:

Included on page :

<script src="/Scripts/jquery-1.7.1.min.js"></script> <script src="/Scripts/jquery.validate.min.js"></script> <script src="/Scripts/jquery.validate.unobtrusive.min.js"></script> 
1
  • Nice answer below. Here's a related question. The answer allows for client-side or server-side validation. I'm in love with the JQuery code they provide. (No, it wasn't my answer.) stackoverflow.com/questions/28987752/…
    – Adventure
    CommentedMar 9, 2017 at 23:38

5 Answers 5

160
+50

Client Side

Using the jQuery.validate library should be pretty simple to set up.

Specify the following settings in your Web.config file:

<appSettings> <add key="ClientValidationEnabled" value="true"/> <add key="UnobtrusiveJavaScriptEnabled" value="true"/> </appSettings> 

When you build up your view, you would define things like this:

@Html.LabelFor(Model => Model.EditPostViewModel.Title, true) @Html.TextBoxFor(Model => Model.EditPostViewModel.Title, new { @class = "tb1", @Style = "width:400px;" }) @Html.ValidationMessageFor(Model => Model.EditPostViewModel.Title) 

NOTE: These need to be defined within a form element

Then you would need to include the following libraries:

<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> 

This should be able to set you up for client side validation

Resources

Server Side

NOTE: This is only for additional server side validation on top of jQuery.validation library

Perhaps something like this could help:

[ValidateAjax] public JsonResult Edit(EditPostViewModel data) { //Save data return Json(new { Success = true } ); } 

Where ValidateAjax is an attribute defined as:

public class ValidateAjaxAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if (!filterContext.HttpContext.Request.IsAjaxRequest()) return; var modelState = filterContext.Controller.ViewData.ModelState; if (!modelState.IsValid) { var errorModel = from x in modelState.Keys where modelState[x].Errors.Count > 0 select new { key = x, errors = modelState[x].Errors. Select(y => y.ErrorMessage). ToArray() }; filterContext.Result = new JsonResult() { Data = errorModel }; filterContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.BadRequest; } } } 

What this does is return a JSON object specifying all of your model errors.

Example response would be

[{ "key":"Name", "errors":["The Name field is required."] }, { "key":"Description", "errors":["The Description field is required."] }] 

This would be returned to your error handling callback of the $.ajax call

You can loop through the returned data to set the error messages as needed based on the Keys returned (I think something like $('input[name="' + err.key + '"]') would find your input element

6
  • 1
    Great answer, especially with the awesome ValidateAjaxAttribute! Thank you!
    – René
    CommentedFeb 4, 2013 at 14:19
  • 4
    I don't understand why this answer got so many votes. It doesn't answer question 1: how to do client validation when posting with $.ajax? I think @Shyju answer helps with that.
    – Valentin
    CommentedJul 22, 2014 at 12:10
  • 2
    @Valentin - my answer does help though because the data is validated server side as well. The validation plugins should enable dynamic validation as the form is filled out, and when the form is submitted (however the OP wants to do that), the server will provide final validation, which is preferable anyways since client side validation can be bypassed.CommentedJul 23, 2014 at 8:14
  • 10
    I utilise jQuery's validation message spans by looping through the errors returned and inserting the error message into the correct span: for (var i = 0; i < modelStateErrors.length; i++) { $('span[data-valmsg-for="' + modelStateErrors[i].key + '"]').text(modelStateErrors[i].errors[0]); }
    – Ian
    CommentedOct 15, 2015 at 13:52
  • 10
    This answer is great! But I still think the ASP.NET MVC framework should provide a built-in way of doing that.
    – Zignd
    CommentedNov 29, 2015 at 4:42
42

What you should do is to serialize your form data and send it to the controller action. ASP.NET MVC will bind the form data to the EditPostViewModel object( your action method parameter), using MVC model binding feature.

You can validate your form at client side and if everything is fine, send the data to server. The valid() method will come in handy.

$(function () { $("#yourSubmitButtonID").click(function (e) { e.preventDefault(); var _this = $(this); var _form = _this.closest("form"); var isvalid = _form .valid(); // Tells whether the form is valid if (isvalid) { $.post(_form.attr("action"), _form.serialize(), function (data) { //check the result and do whatever you want }) } }); }); 
7
  • 1
    Thanks! I tried this $("form#" + formId).validate() but it says that the form(that is found) do not have a validate()?
    – Ivy
    CommentedDec 28, 2012 at 10:22
  • See Edit1 where I show that the validate script is included on the webpage. I also see that the validation is running when using a regular input type submit button.
    – Ivy
    CommentedDec 28, 2012 at 10:34
  • I found the problem(removed Scripts.Render from masterpage). But still have problems to send back ModelState validation errors to the client? How am I supose to thandle that? For example if the user is not loggedin anymore( ModelState.AddModelError("CustomError", "validation text").
    – Ivy
    CommentedDec 28, 2012 at 13:48
  • 1
    you need to include the jquery.validate and jquery.validate.unobtrusive js files in your page. IS your HTML inputs have the attribute which validation plugins look for ?
    – Shyju
    CommentedDec 28, 2012 at 14:51
  • 1
    Ivy : Your return type (ActionResult) is a base class of JsonResult. so it can return JSON data. What you should do is, If it is an ajax call (check Request.IsAjax), get the validation errors and build a JSON and send it back to client. Check the json in the callback method of $.post and show the error messages.
    – Shyju
    CommentedDec 29, 2012 at 23:52
10

Here's a rather simple solution:

In the controller we return our errors like this:

if (!ModelState.IsValid) { return Json(new { success = false, errors = ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToList() }, JsonRequestBehavior.AllowGet); } 

Here's some of the client script:

function displayValidationErrors(errors) { var $ul = $('div.validation-summary-valid.text-danger > ul'); $ul.empty(); $.each(errors, function (idx, errorMessage) { $ul.append('<li>' + errorMessage + '</li>'); }); } 

That's how we handle it via ajax:

$.ajax({ cache: false, async: true, type: "POST", url: form.attr('action'), data: form.serialize(), success: function (data) { var isSuccessful = (data['success']); if (isSuccessful) { $('#partial-container-steps').html(data['view']); initializePage(); } else { var errors = data['errors']; displayValidationErrors(errors); } } }); 

Also, I render partial views via ajax in the following way:

var view = this.RenderRazorViewToString(partialUrl, viewModel); return Json(new { success = true, view }, JsonRequestBehavior.AllowGet); 

RenderRazorViewToString method:

public string RenderRazorViewToString(string viewName, object model) { ViewData.Model = model; using (var sw = new StringWriter()) { var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName); var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw); viewResult.View.Render(viewContext, sw); viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View); return sw.GetStringBuilder().ToString(); } } 
2
  • 3
    Your "GetModelStateErrors" can be simplified completely by converting it to a way simpler single-line statement: return ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToList();CommentedSep 3, 2016 at 0:44
  • 1
    Why not simply return a PartialView to render to Ajax?
    – Sinjai
    CommentedAug 24, 2017 at 20:18
5

Added some more logic to solution provided by @Andrew Burgess. Here is the full solution:

Created a action filter to get errors for ajax request:

public class ValidateAjaxAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if (!filterContext.HttpContext.Request.IsAjaxRequest()) return; var modelState = filterContext.Controller.ViewData.ModelState; if (!modelState.IsValid) { var errorModel = from x in modelState.Keys where modelState[x].Errors.Count > 0 select new { key = x, errors = modelState[x].Errors. Select(y => y.ErrorMessage). ToArray() }; filterContext.Result = new JsonResult() { Data = errorModel }; filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } } 

Added the filter to my controller method as:

[HttpPost] // this line is important [ValidateAjax] public ActionResult AddUpdateData(MyModel model) { return Json(new { status = (result == 1 ? true : false), message = message }, JsonRequestBehavior.AllowGet); } 

Added a common script for jquery validation:

function onAjaxFormError(data) { var form = this; var errorResponse = data.responseJSON; $.each(errorResponse, function (index, value) { // Element highlight var element = $(form).find('#' + value.key); element = element[0]; highLightError(element, 'input-validation-error'); // Error message var validationMessageElement = $('span[data-valmsg-for="' + value.key + '"]'); validationMessageElement.removeClass('field-validation-valid'); validationMessageElement.addClass('field-validation-error'); validationMessageElement.text(value.errors[0]); }); } $.validator.setDefaults({ ignore: [], highlight: highLightError, unhighlight: unhighlightError }); var highLightError = function(element, errorClass) { element = $(element); element.addClass(errorClass); } var unhighLightError = function(element, errorClass) { element = $(element); element.removeClass(errorClass); } 

Finally added the error javascript method to my Ajax Begin form:

@model My.Model.MyModel @using (Ajax.BeginForm("AddUpdateData", "Home", new AjaxOptions { HttpMethod = "POST", OnFailure="onAjaxFormError" })) { } 
    1

    You can do it this way:

    (Edit: Considering that you're waiting for a response json with dataType: 'json')

    .NET

    public JsonResult Edit(EditPostViewModel data) { if(ModelState.IsValid) { // Save return Json(new { Ok = true } ); } return Json(new { Ok = false } ); } 

    JS:

    success: function (data) { if (data.Ok) { alert('success'); } else { alert('problem'); } }, 

    If you need I can also explain how to do it by returning a error 500, and get the error in the event error (ajax). But in your case this may be an option

    3
    • 1
      I know how to do a regular Jason request, this will however not help me with validation of the diffrent properties or even use the built in ASP.NET MVC Validation that I asked for. I could probably build a complexed Jason object to explain the validation errors for every propertie but this will be alot of work and I am hoping that you can reuse the built in functionality of ASP.NET MVC validation for this?
      – Ivy
      CommentedDec 22, 2012 at 21:10
    • 1 - How to run your function "SendPost"?, And 2 - your valid data on the client?CommentedDec 22, 2012 at 21:34
    • Pleas explain? Dont get your last post?
      – Ivy
      CommentedDec 23, 2012 at 10:19

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.