Saad
Saad

Reputation: 45

Add ModelState.AddModelError to the view after submitting form through javascript

I'm new to web development. Currently I'm working on a practice project on asp.net mvc 5. I have a form for users where the admin can add a new user to the system. The new user form opens in a modal and is submitted in the form format

VIEW

@using (Html.BeginForm("AddUser", "User", FormMethod.Post, new { onsubmit = "return SubmitForm(this)" }))

{

@Html.ValidationSummary(true, "", new { @class = "text-danger" })

@Html.AntiForgeryToken()
<div class="form-horizontal">
    <div class="modal-header">
        <h4 class="modal-title" id="myModalLabel">Add New User</h4>
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.user_name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-8">
            @Html.EditorFor(model => model.user_name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.user_name, "", new { @class = "text-danger" })
            @Html.ValidationMessage("UserExist", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.role_id, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.role_id, new SelectList(ViewBag.roles, "role_id", "role_name"), "Select", new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.role_id)
        </div>
    </div>  


    <div class="form-group">
        @Html.LabelFor(model => model.user_password, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.user_password, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.user_password, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(model => model.confirmPassword, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.confirmPassword, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.confirmPassword, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(model => model.user_email, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.user_email, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.user_email, "", new { @class = "text-danger" })
            @Html.ValidationMessage("EmailExist", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.supervisor, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.supervisor, new SelectList(ViewBag.supervisors, "user_id", "user_name"), "Select Supervisor", new { @class = "form-control" })
        </div>

    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.region_id, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.region_id, new SelectList(ViewBag.regions, "region_id", "region_name"), "Select Region", new { @class = "form-control" })
        </div>
        @Html.ValidationMessageFor(model => model.region_id, "", new { @class = "text-danger" })
    </div>

    <div class="form-group">
        <input type="submit" id="submitBtn" value="Submit" class="btn btn-primary" />
        <input type="reset" value="Reset" class="btn" />
    </div>

</div>

}

Javascript Submit Function

function SubmitForm(form) {
        if ($(form).valid()) {
            $.validator.unobtrusive.parse(form);
            $.ajax({
                type: "POST",
                url: form.action,
                data: $(form).serialize(),
                success: function (data) {
                    if (data.success) {
                        $('#myModal').modal('hide');
                        //Popup.dialog('close');
                        dataTable.ajax.reload();

                        //notify.js plugin
                        $.notify(data.message, {
                            globalPosition: "top center",
                            className: "success"
                        })
                    }
                }                    
            });
        }
        return false;
    }

on submit the form goes to the POST action result which works fine and new user is created but when I try to add model error

#region User Already Exists
            var isUserExists = isUserExist(newUser.user_name);
            if (isUserExists)
            {
                ModelState.AddModelError("UserExist", "User already exists");
                return View(newUser);
            }
            #endregion

the Model error is not reflected in the view. After debugging I found that my model from POST action result is not returned and the previous model is used. I have been dealing with this issue for a long time now, found some possible solutions but none of them seem to work. Any help or sense of direction will be appreciated.

Using remote here is the ViewModel

public class AddUser
{
    [Display(Name = "User name")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "User name is required")]
    public string user_name { get; set; }

    [Display(Name = "Role")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "Confirm Password is required")]
    public int role_id { get; set; }

    [Display(Name = "Password")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "Password is required")]
    [DataType(DataType.Password)]
    [MinLength(6, ErrorMessage = "Minimum 6 characters required")]
    public string user_password { get; set; }


    [Display(Name = "Confirm Password")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "Confirm Password is required")]
    [DataType(DataType.Password)]
    [System.ComponentModel.DataAnnotations.Compare("user_password", ErrorMessage = "Confirm password and password do not match")]
    public string confirmPassword { get; set; }

    [Display(Name = "Email")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "Email is required")]
    [DataType(DataType.EmailAddress)]
    [System.Web.Mvc.Remote("CheckExistingEmail", "ModelValidation", HttpMethod = "POST", ErrorMessage = "Email already exists")]
    public string user_email { get; set; }

    [Display(Name = "Supervisor")]
    public Nullable<int> supervisor { get; set; }

    [Display(Name = "Region")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "User Region is required")]
    public int region_id { get; set; }


}

and here is the ValidationController

public class ModelValidationController : Controller
{
    [AllowAnonymous]
    [HttpPost]
    public ActionResult CheckExistingEmail(string Email)
    {
        try
        {
            return Json(!IsEmailExist(Email));
        }
        catch (Exception ex)
        {
            return Json(false);
        }
    }

    [NonAction]
    public bool IsEmailExist(string email)
    {
        using (mmpEntities mP = new mmpEntities())
        {
            var v = mP.users.Where(a => a.user_email == email).FirstOrDefault();
            //return v == null ? false : true;
            return v != null;
        }
    }

}

Upvotes: 0

Views: 2641

Answers (2)

user10038293
user10038293

Reputation:

Here's what I do inside my controller to send the model state back to the client via success (or done) in ajax:

if (ModelState.IsValid)
{
   //your post code to db

}
else
{
   var modelState = ModelState.ToDictionary
   (
       kvp => kvp.Key,
       kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()

   );
  return Json(new { modelState }, JsonRequestBehavior.AllowGet);
}

Then in the ajax success or done callback do something like this:

 if (data.modelState) {
     $.each(data.modelState, function (i, item) {
       item.valid(); //run jQuery validation to display error
     });
  }

Upvotes: 0

Ali Dogan
Ali Dogan

Reputation: 116

For the validation you can use jquery module unobtrusive js, and it will give you live jquery error functionality when you type misleading input. Here is how you can use it:

<script src=" jquery.validate.min.js"></script>
<script src="jquery.validate.unobtrusive.min.js"></script>
<script src="~/vendors/jquery.form.min.js"></script>
<script>
  $(document).ready(function () {
            $(document).on("click", "#Subscribe", function () {
                $('#SubscribeForm').ajaxForm({
                    success: function (response) {
                        if (response.Success) {
// You could use toastr notifaction in here } 
else {
                          // You could use toastr notifaction in here
                        }
                    }
                })
            })
        })
</script>

Upvotes: 1

Related Questions