PhillyNJ PhillyNJ - 21 days ago 13
Ajax Question

AJAX Model Validation with Partial View

I have a partial view, which is a login that functions as a popup. All I want to do is have my model do the validation (server side) and return any errors via AJAX. The code below returns the partial view only with the errors. I want my action result to not return a a view, but only the errors. In old ASP.NET, this would be a Partial Post back. I am not sure how to accomplish this in MVC.

Here is the Model

public class LoginModel
{
[Required]
public String Email { get; set; }
[Required]
[DataType(DataType.Password)]
public String Password { get; set; }

}


Here is the Partial View

@model MySite.Models.LoginModel
@using (Ajax.BeginForm("Authenticate", "Account", null, new AjaxOptions { OnFailure = "error" }, new { id = "LoginForm" }))


{

<div class="modal-body" id="LoginPopupDialogMessage">
The page you have requested requires you to login. Please enter your credentials and choose your country:
<br />
<br />
<div class="row">
<div class="form-group col-lg-offset-2 col-lg-8">
<label>Email Address</label>
@Html.TextBoxFor(u => u.Email, new { @class = "form-control input-lg input-sm", id = "Email", name = "Email" })
@Html.ValidationMessageFor(u => u.Email)
</div>
</div>

<div class="row">
<div class="form-group col-lg-offset-2 col-lg-8 ">
<label>Password</label>
@Html.PasswordFor(u => u.Password, new { @class = "form-control input-lg input-sm", name = "Password" })
@Html.ValidationMessageFor(u => u.Password)
</div>

</div>
<div style="text-align: center; padding-top: 20px;" class="ImageGroup">
<button name="companyCode" value="LB_US" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-usa.png" />
</button>
<button name="companyCode" value="LB_CA" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-canada.png" />
</button>
<button name="companyCode" value="LB_EU" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-europe.png" />
</button>
</div>
</div>
}


I call the parial view from
_layout.cshtml.


<div class="modal fade" id="LoginPopupDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header" style="background: #e7e3e7; color:#000;">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="color:#000;">
<span aria-hidden="true">&times;</span>
</button>
<div class="modal-title" id="LoginPopupDialogHeader">Please Login</div>
</div>
@Html.Action("Login", "Account")

</div>
</div>
</div>


My Controller Action:

[HttpPost]
[Route("account/authenticate")]
public ActionResult Authenticate(String companyCode, LoginModel model)
{
if (!ModelState.IsValid)
{

// ??
}
return PartialView("Login", model);
}

Answer

Since your code is doing an ajax form submission for the login, you should try to return a JSON response from the server. If model validation fails, you may read the validation errors from the model state dictionary and store that in a collection of strings (error messages) and return that as part of the json response. If model validation passes, you can continue executing your code to verify the login credentials and if those looks good, send back a json response with the next url for the user (to which we can redirect the user).

[HttpPost]
public ActionResult Authenticate(String companyCode, LoginModel model)
{
    if (!ModelState.IsValid)
    {
        var errors = ViewData.ModelState.Values
                             .SelectMany(x => x.Errors.Select(c => c.ErrorMessage));
        return Json(new { Status = "Error", Errors = errors });
    }

    //to do  :Verify login, if good, return the below respose
    var url=new UrlHelper(Request.RequestContext);
    var newUrl = url.Action("About");
    return Json(new { Status="Success", Url = newUrl});
}

Now in your view, you may specify a OnSuccess handler as part of the AjaxOptions. This will be a javascript object to which the json response from the server will come. We basicallly need to check the Status property value and do the appropriate things.

new AjaxOptions { OnFailure = "error" , OnSuccess="loginDone"}

The below implementation of loginDone simply alerts the error messages. You can update it to show it as part of the DOM.

function loginDone(d) {
    if (d.Status === "Success") {
        window.location.href = d.Url;
    } else {
        $.each(d.Errors,function(a, b) {
                alert(b);
        });
    }
}

You may also consider enabling the unobtrusive client side validation which does the client side validation before trying to make a call to server. This will also show the error messages in the validation error spans (same as the normal mvc model validation does)

Comments