Ajax.BeginForm with Dropdown + Server Side Validation

684 views Asked by At

I am trying to build an Ajax.BeginForm (that has a dropdown) which uses server side validation that checks the model's annotations. I am having trouble getting the dropdown to work properly. Originally I populated the dropdown using the viewbag as such:

view:

@Html.DropDownList("CheckIn_Location", ViewBag.CageLocationList as IEnumerable<SelectListItem>, "(Select one)", new { @class = "form-control" })

controller:

    public void GetCageLocations()
    {        
        IEnumerable<SelectListItem> selectList =
        from c in db.Locations
        select new SelectListItem
        {
            Text = c.LocationName,
            Value = c.Id.ToString()
        };

        ViewBag.CageLocationList = selectList;
    }

But that didn't seem to play friendly with server side validation so I am tried reworking my model/view/controllers as such:

Here is my Model:

public class CheckInViewModel
{
    public int CheckIn_Id { get; set; }

    [Required(ErrorMessage = "Location Required.")]
    public IEnumerable<SelectListItem> CheckIn_Location { get; set; }

    [Required(ErrorMessage = "Quantity Required.")]
    [Range(1, 100, ErrorMessage = "Quantity must be between 1 and 100000")]
    public int CheckIn_Quantity { get; set; }

    public string CheckIn_Comment { get; set; }

}

Here is my Controller:

    [HttpPost]
    public ActionResult CheckIn(CheckInViewModel model)
    {
        if (ModelState.IsValid)
        {

            var New_Transaction = new Transaction
            {
                Id = model.CheckIn_Id,
                Quantity = model.CheckIn_Quantity,
                LocationId = Convert.ToInt32(model.CheckIn_Location),
                TransactionDate = DateTime.Now,
                TransactionComments = model.CheckIn_Comment.Replace("\r\n", " ")
            };

            unitOfWork.TransactionRepository.Insert(New_Transaction);
            unitOfWork.Save();

            return PartialView("CheckIn", model);
        }

        return PartialView("CheckIn", model);

    }

Here is my PartialView called CheckIn.cshtml

@model ViewModels.CheckInViewModel

<!-- Modal -->
<div class="modal fade" id="CheckInModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                <h1 class="modal-title" id="CheckInModalLabel">Check In</h1>
            </div>
            <div class="modal-body">

            @using (Ajax.BeginForm("CheckIn", "Cage", null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "success", OnFailure  = "failure"}, new { @id = "CheckInForm", @class = "form-horizontal" }))
            {

                @Html.ValidationSummary(true)

                @Html.HiddenFor(model => model.CheckIn_Id, new { @class = "form-control" })

                <div class="form-group">
                    <label for="CheckIn_Location" class="col-sm-4 control-label">Location</label>
                    <div class="col-sm-8">
                        @Html.DropDownListFor(x => x.CheckIn_Location, Model.CheckIn_Location, "Select One")
                        @Html.ValidationMessageFor(model => model.CheckIn_Location)
                    </div>
                </div>

                <div class="form-group">
                    <label for="CheckIn_Quantity" class="col-sm-4 control-label">Quantity</label>
                    <div class="col-sm-8">
                        @Html.TextBoxFor(model => model.CheckIn_Quantity, new { @class = "form-control" })
                        @Html.ValidationMessageFor(model => model.CheckIn_Quantity)
                    </div>
                </div>

                <div class="form-group">
                    <label for="CheckIn_Comment" class="col-sm-4 control-label">Comment</label>
                    <div class="col-sm-8">
                        @Html.TextAreaFor(model => model.CheckIn_Comment, new { @class = "form-control" })
                    </div>
                </div>
            }

        </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary" onclick="SubmitCheckInForm()">Check In</button>
            </div>
        </div>
    </div>
</div>

here is the JS function that fire the submit:

    function SubmitCheckInForm() {
            $('#CheckInForm').submit();
        }

Can someone show me how to:

  1. populate the dropdown with (value/text) using the model as the binding agent (not the viewbag as I previously did it)
  2. return the selected option to the controller and insert it into the transaction table's location element (which is an int)
  3. properly hook this all up so the server side annotations work and return messages when something is incorrect in the form.

Thanks in Advance!

1

There are 1 answers

0
osotorrio On

If I have understood you correctly, there is more than one way to do this. For instance, you could have two properties in your model to manage the Dropdown control.

1- CheckIn_Location_Selected. To store the value selected by the user

2- CheckIn_Location_List. To fill the DropdownList.

This could be your model.

public class CheckInViewModel
{
    [Required(ErrorMessage = "Location Required.")]
    public int CheckIn_Location_Selected { get; set; } 

    public IEnumerable<SelectListItem> CheckIn_Location_List { get; set; }

    //Rest of the properties...
}

So now in your GET action you could have something like this:

    [HttpGet]
    public ActionResult CheckIn()
    {
        var model = new CheckInViewModel
        {
            CheckIn_Location_List = repository.GetCageLocations().Select(
                location => new SelectListItem
                {
                    Value = location.Id.ToString(),
                    Text = location.LocationName
                })
        };

        return View(model);
    }

And in your view:

@Html.DropDownListFor(x => x.CheckIn_Location_Selected, Model.CheckIn_Location_List, "Select One")

We need to change a bit your POST action.

[HttpPost]
    public ActionResult CheckIn(CheckInViewModel model)
    {
        if (!ModelState.IsValid)
        {
            // This is necessary because we are sending the model back to the view.
            // You could cache this info and do not take it from the DB again.
            model.CheckIn_Location_List = repository.GetCageLocations().Select(
                location => new SelectListItem
                {
                    Value = location.Id.ToString(),
                    Text = location.LocationName
                });

            return PartialView("CheckIn", model);
        }

        var New_Transaction = new Transaction
        {
            Id = model.CheckIn_Id,
            Quantity = model.CheckIn_Quantity,
            LocationId = Convert.ToInt32(model.CheckIn_Location_Selected),
            TransactionDate = DateTime.Now,
            TransactionComments = model.CheckIn_Comment.Replace("\r\n", " ")
        };

        unitOfWork.TransactionRepository.Insert(New_Transaction);
        unitOfWork.Save();

        return PartialView("CheckIn", model);
    }