1

I'd so appreciate if someone could advise.

I have an Ajax form:

  @using (Ajax.BeginForm("Edit", null, new AjaxOptions() { 
                                       UpdateTargetId = updateRegion, 
                                       InsertionMode = InsertionMode.Replace, 
                                       OnFailure = "formFailure" }))
    {}

Its UpdateTargetId differs based on the current User role:

@{
   if (curUser.IsInputer)
    {
        updateRegion = "content";
    }
    else if (curUser.IsAuthorizer)
    {
        updateRegion = "mainPane";
    }
}

If the modelstate is invalid I'd like to return view in mainPane always:

<script>
function formFailure(result)
{
    $("#mainPane").html(result.responseText);        
}
</script>

However onFailure is not called when ModelState is invalid. For this, I set error code in controller:

public ActionResult Edit(ContractModel entity)
 {
    if(ModelState.IsValid)
    { 
       if(curUser.isInputer) { return RedirectToAction("SomeAction");}
       if(curUser.Authorizer) { return RedirectToAction("OtherAction");}
    }

     Response.StatusCode = 500;//internal server error to fire OnFailure of form
     return PartialView(entity);
 }

Then I get the desired result, i.e. I see the model with its errors in mainPane div and internal server error in browser console. However, it works this way when I run the application locally, when I publish and run it on the server, I see the error 500 Internal server error, instead of the partial view. Is there any workaround?

EDIT: As an option I tried to check Model for errors in form OnSuccess handler:

    var isValid = @Html.Raw(Json.Encode(ViewData.ModelState.IsValid));

        if (!isValid) {

            $("#mainPane").html(result.responseText);    
        }

But I still get the view in "content" div. Why is this happening?

Gyuzal
  • 1,581
  • 10
  • 52
  • 99

2 Answers2

1

Here is an option:

Dont use a 500 status error. That is for reporting something going very wrong with your application. That is not what has happened here, you simply have a form validation error.

Update your server side code to this:

public ActionResult Edit(ContractModel entity)
 {
    if(ModelState.IsValid)
    { 
       if(curUser.isInputer) { return RedirectToAction("SomeAction");}
       if(curUser.Authorizer) { return RedirectToAction("OtherAction");}
    }

    if(!ModelState.IsValid)
    {
        entity.FormError = true; // you will need to add this attribute to your model
    }

    return PartialView(entity);
}

Then in your partial view, put something like this:

if(Model.FormError)
{
    @Html.HiddenFor(m => m.FormError)
}

Change your form to handle the OnComplete event instead of OnFailure event. This is because your ajax post is not failing. Its returning successfully and reporting there was a validation error with the forms input:

@using (Ajax.BeginForm("Edit", null, new AjaxOptions() { OnSuccess = "handleFormResponse" }))
  {}

Change your handler script to something like this. This will check the response to see if contains a form error

  <script>
  function handleFormResponse(result)
  {
        $(result).find("#FormError").length > 0)
        {
            $("#mainPane").html(result);  
        }
        else
        {
            $("#@updateRegion").html(result);
        }

  }
  </script>
Keith
  • 718
  • 8
  • 12
  • Thanks for your reply, but it works the same way like in my last edit. I see the responseText in "content" div. – Gyuzal Jan 13 '15 at 05:16
  • I don't think it does. You are using: var isValid = @Html.Raw(Json.Encode(ViewData.ModelState.IsValid)) which sets "isValid" ONCE, when the page loads for the first time. The ajax form does not reload the page, so that line of does not get executed again. You need to send ModelState.IsValid back to the client again after the ajax call... as per my example above. – Keith Jan 13 '15 at 10:34
  • Ok, agree. I tried your answer, it doesn't work for me, if formError true I still get all the content in "content" div. – Gyuzal Jan 13 '15 at 11:13
  • Can you see a hidden input with Id="FormError" in the markup that is returned after the form post? – Keith Jan 13 '15 at 12:50
  • Yes, and onComplete handler also fires. – Gyuzal Jan 14 '15 at 05:01
  • 1
    ok, that sounds like we are not using the result object correctly. One last suggestion from me is to remove the "UpdateTargetId" parameter completely and handle the HTML population in the OnSuccess Method. Ill edit my original answer to reflect this. This approach is used here: http://stackoverflow.com/questions/12562857/ajax-beginform-with-oncomplete-always-updates-page – Keith Jan 15 '15 at 10:02
0

This is likely happening because the server is interpreting the 500 as an unhanded error and returning the default 500 error since your customErrors setting in the web config likely gets flipped to false when you publish the application in release mode.

Edit: Removed mention of using another status code as it caused more confusion and does not solve the problem.

Jarga
  • 391
  • 3
  • 9
  • I still see the error "400 Bad Request". I think setting Response.StatusCode is not an option, I should see the model with errors not the error message. – Gyuzal Jan 12 '15 at 04:55
  • You should not need the response code at all, the Model errors should show for you even without it and the javascript method due to the `InsertionMode` parameter if i am not mistaken. Is there a reason you wanted to use a response code? – Jarga Jan 12 '15 at 05:20
  • Yeah, I know I can see model errors without response code, but my problem is that I use different UpdateTargetIds based on current user role. But if the model returns some errors, I need to stay in "mainPane" div, regardless the role. – Gyuzal Jan 12 '15 at 05:51
  • 1
    Can you make the PartialView contain both `updateRegions` and then just have a single update target? The alternatives i can think of would either be to change the ActionResult to a json result containing a `status` and the view as a string. Or to parse the output from the server for a hidden input that you set in the partial view if there are model errors. [See the responses to this question](http://stackoverflow.com/questions/10589787/asp-net-mvc-returning-a-partialview-to-ajax-along-with-another-object) – Jarga Jan 12 '15 at 06:12
  • Is it ok to return Json in HttpPost action? Then I should sumbit the form in jQuery? I didn't get your last point about parsing the output, could you please provide an example. – Gyuzal Jan 12 '15 at 06:23
  • I agree with @Jarga. The best approach is probably to put both content and mainPane in the partial view and make the decision which to show on the server side in your Razor view. But to get it working the way you have it... ModelState is a server side value and formFailure() is a client side function. You need to put the ModelState value you are interested into the response html. Then in your javascript handler, check for its presence before deciding which target to update. Also, OnComplete might be a better handler to use, since it fires before the page is updated. – Keith Jan 12 '15 at 17:48