1

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);        
};
Joel
  • 647
  • 2
  • 11
  • 22

4 Answers4

4

I solved this by doing the following...although I don't know if this is a suggested way to handle it. Also, you should return the partial view in both cases.

Before returning the partial view add the following line

Response.AppendHeader("X-Error", "true")

Then in your javascript onsuccess

function doOnSuccess(data, status, xhr) {
   if (xhr.getResponseHeader('X-Error')) {
      //validation error occurred
   }
   else
   {
      //validation error did not occur
   }
}
  • Ryan, I just saw this response, my apologies. I ended up doing something different though really in the same vein as what you suggest. If validation fails, I get the same form back with validation errors. If the post is successful I get the succes view. So, in the javascript success function I added if (data.indexOf("form action=") === -1) {//success stuff } else { //redisplay the form } – Joel Jan 03 '14 at 05:14
0

Instead of using the Ajax.BeginForm I would use a normal form and use an ajax call for this as well.

$(document).on('click', '.btn-primary', function(){
    $.ajax({
        url: '@Url.Action('QuickPane', 'QuizPane')',
        type: 'post',
        data: {
            Title: $('#Title').val()
        }
        success: function(result){
            //Do Something
        }
        error: function(result){
            //Display error
        }
    });
});

then in your controller return JSON instead of partial view and you can set the error like this return Json error from ASP.NET MVC or success like this ExtJS: how to return json success w/ data using asp.net mvc Hopefully this helps.

Community
  • 1
  • 1
Matt Bodily
  • 6,403
  • 4
  • 29
  • 48
  • In that scenario, aren't I losing all the benefits of the model validation and the accompanying messages for required fields etc? Why does everything have to be so hard? :) – Joel Oct 28 '13 at 19:33
  • The only way I have been successful in getting the validation to show on a partial view is to change it to a full view and show it in an iframe. You can do client side jquery validation using this method and manually do server side validation and send the results through error but it is a little more work. This has been a tricky scenario for me as well – Matt Bodily Oct 28 '13 at 19:38
  • I edited my question slightly because I'm actually doing return View(quizpane); instead of PartialView. If I add UpdateTargetId = "overlay" to my Ajax.BeginForm it does validation properly when ModelState.IsValid==false. But when ModelState.IsValid==true it fails because it never calls the OnSuccess method, it just inserts the result into #overlay which I don't want. If I remove UpdateTargetId = "overlay" it works properly when ModelState.IsValid==true, but fails when ModelState.IsValid==false because I don't get the validation errors in the form. Jeebus! – Joel Oct 28 '13 at 21:14
0

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:

KyleMit
  • 30,350
  • 66
  • 462
  • 664
0

Just use if/else in the success of the result

       $("#SaveStudentRecord").click(function () {
            var data = $("#SubmitForm").serialize();  
            $.ajax({
                method: "POST",
                url: '@(Url.Action("EditAdd", "Ucesnik"))',
                data: data,
                success: function (result) {
                    if (result.success) {
                        alert("Success!..");
                        window.location.href = "/Ucesnik/GetUcesnici";
                        $("#MyModal").modal("hide");
                    }
                    else {
                        alert("Not successful");
                    }
                }
            })
        })
N Kostic
  • 41
  • 5