This is a well-worn topic apparently but I've not been able to come up with a solution perusing previous posts. Hoping someone can tell me the "right way" to do this, since I need to do a lot of it.

Main issue: When Ajax.Beginform post ModelState is invalid, it still triggers OnSuccess method. I know this is correct behavior, but my OnSuccess method does things that only make sense when (go figure) post is "successful."

How best to manage redisplaying form with validation errors in this case? How do I return a success=false from my controller along with the view to be redisplayed?

It's not specifically relevant to the question, but fyi what I'm doing here is somewhat analagous to trying to display a form in a Bootstrap Modal, which I gave up on.

  1. calling controller via ajax to load form in div.

    $(function addpaneevent() { $('.addpane').on("click", function () { var url = '/QuizPane/QuickPane?quizId=' + $(this).data("quizid"); $.ajax({ url: url, type: 'GET', contentType: 'application/json', success: function (result) { $('#overlay').html(result); }, error: function (result) { console.log("bummer",result); } }); }); });

  2. return partial view containing form

    public ActionResult QuickPane(int quizId) { var model = querytogetmodel return PartialView("QuickPane",model); }

    @using (Ajax.BeginForm("QuickPane", "QuizPane", FormMethod.Post, new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", OnSuccess = "updatePaneList(data)", OnFailure = "massivefail(data)", })) { @Html.ValidationSummary(true)

     <fieldset>
         @Html.HiddenFor(model => model.QuizID)
         <div class="editor-label">
             @Html.LabelFor(model => model.Title)
         </div>
         <div class="editor-field">
             @Html.TextBoxFor(model => model.Title, new { @autofocus = "autofocus" })
             @Html.ValidationMessageFor(model => model.Title)
         </div>
         ...more of the same
         <input type="submit" value="Create" class="btn btn-primary"/>
     </fieldset>
    

    }

  3. On submit call post

    [HttpPost] public ActionResult QuickPane(QuizPane quizpane) { if (ModelState.IsValid) { db.QuizPanes.Add(quizpane); db.SaveChanges(); return PartialView("_quizpanelist", quizpane); } return View(quizpane); //tried a few different things here }

  4. On success I'm adding the new record to a table and removing the div containing the input form.

    function updatePaneList(data) { $("#overlay").remove(); var rowcount = $(".expanded #panetable tr:last").count; $('.expanded #panetable tbody:last').append(data);
    };

That's because OnSuccess vs OnFailure aren't hooked up to ModeState.IsValid in any way.

Per the AjaxOptions docs:

OnSuccess: This function is called if the response status is in the 200 range.
OnFailure: This function is called if the response status is not in the 200 range.

In other words, you have to manually specify that there was a failure when returning your ActionResult by changing the Response.StatusCode and then returning whatever values you're expecting in your OnFailure js method.

[HttpPost]
public ActionResult Search(Person model) 
{
  if (ModelState.IsValid) {
    // if valid, return a HTML view inserted by AJAX helper
    var results = PersonRepository.Get(model)
    return PartialView("Resulsts", vm);
    
  } else {
  	// if invalid, return a JSON object and handle with OnFailure method
    Response.StatusCode = (int)HttpStatusCode.BadRequest;
    return Json(new { errors = ModelState.Values.SelectMany(v => v.Errors) });

  }
}

Further Reading: