Home MVC 4 Detail Records Won't Bind
Reply: 0

MVC 4 Detail Records Won't Bind

Eric Schilling
1#
Eric Schilling Published in 2017-12-04 23:56:12Z

I am stuck with a master/detail type of problem in my "CreateOrder" view. The customer has specified that they want a specific set of detail records based on the selection from a dropdown. Based on the selection, 2, 3 or 4 detail records will be displayed for the user to enter information. The user fills out the entire form with both information in the "master" part and the details part. When they are done, they click the "Create" button. The problem is, the only data that gets to the controller is the "master" part.

I know I may be giving out too much info but this is pared down from the actual app. I'm not sure what is necessary to solve the problem so I am posting everything. Let's start with the models...

public class Order
{
    public int OrderID { get; set; }
    public string CustomerName { get; set; }
    public DateTime OrderDate { get; set; }
    public DateTime RequiredBy { get; set; }
    public string OrderStatus { get; set; }
}

public class OrderDetails
{
    public int OrderDetailID { get; set; }
    public int OrderID { get; set; }
    public string ItemName { get; set; }
    public int ItemQty { get; set; }
    public int UnitCost { get; set; }
}

public class MyDDL
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class MyViewModel
{
    public Order Order_Info { get; set; }
    public int SelectedRowCount { get; set; }
    public IList<OrderDetails> OrderItems { get; set; }
}

So the models are pretty straightforward and they mimic the examples I have seen here and elsewhere. My ViewModel combines the Order model, the selected value of the DDL and a list of OrderDetail objects.

Here is the Controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MasterDetailBindingProblem.Models;

namespace MasterDetailBindingProblem.Controllers
{
    public class OrdersController : Controller
    {
        public ActionResult OrdersIndex()
        {
            List<Order> currentOL = new List<Order>();
            return View(currentOL);
        }

        [HttpGet]
        public ActionResult OrdersCreate()
        {
            //Set defaults for create view.
            MyViewModel currentVM = new MyViewModel();
            Order currentOrder = new Order();
            List<OrderDetails> currentItems = new List<OrderDetails>();

            currentVM.Order_Info = currentOrder;
            currentVM.OrderItems = currentItems;

            return View(currentVM);
        }

        [HttpPost]
        public ActionResult OrdersCreate(MyViewModel currentVM)
        {
            //When ready to run, set breakpoint online checking ModelState.  When the code
            //gets there, check the currentVM.  That part of the model will be null.  Even if
            //you put data in the rows, it will still be null.
            if (ModelState.IsValid)
            {
                //Code here to do stuff before saving to database.
                //The problem is that the OrderItems part of the model is null
                //because for some reason it doesn't bind.

                return RedirectToAction("OrdersIndex");
            }
            return View();
        }

        public ActionResult GetItemsPartial(string id)
        {
            //Create a list of id items.
            int rowCount = Convert.ToInt32(id);

            List<OrderDetails> orderDetailList = new List<OrderDetails>();

            //Create the correct number of empty layers.
            for (int i = 0; i < rowCount; i++)
            {
                OrderDetails curItem = new OrderDetails();
                orderDetailList.Add(curItem);
            }

            return PartialView("_OrderDetailsPartial", orderDetailList);
        }
    }
}

If you set a breakpoint at the beginning of the HttpPost for OrdersCreate and look at currentVM, you will note the the OrderDetails attribute is null. It should have the data you just entered but it doesn't.

Here is the Partial view Razor code:

@model IList<MasterDetailBindingProblem.Models.OrderDetails>
    <div>
        @if (Model.Any())
        {
            for (int i = 0; i < Model.Count(); i++)
            {
                var item = Model[i];
                <div>
                    <table>
                        <tr>
                            <th>ItemID</th>
                            <th>WorkOrderID</th>
                            <th>ItemName</th>
                            <th>ItemQTY</th>
                            <th>UnitCost</th>
                        </tr>
                        <tr>
                        <td>
                            <div class="editor-field">
                                @Html.DisplayFor(m => m[i].OrderDetailID)
                                @Html.ValidationMessageFor(m => m[i].OrderDetailID)
                            </div>
                        </td>
                        <td>
                            <div class="editor-field">
                                @Html.EditorFor(m => m[i].OrderID)
                                @Html.ValidationMessageFor(m => m[i].OrderID)
                            </div>
                        </td>
                        <td>
                            <div class="editor-field">
                                @Html.EditorFor(m => m[i].ItemName)
                                @Html.ValidationMessageFor(m => m[i].ItemName)
                            </div>
                        </td>
                        <td>
                            <div class="editor-field">
                                @Html.EditorFor(m => m[i].ItemQty)
                                @Html.ValidationMessageFor(m => m[i].ItemQty)
                            </div>
                        </td>
                        <td>
                            <div class="editor-field">
                                @Html.EditorFor(m => m[i].UnitCost)
                                @Html.ValidationMessageFor(m => m[i].UnitCost)
                            </div>
                        </td>
                    </tr>
                </table>
            </div>
            }
        }
        else
        {
            <p>Please select an option to see layers.</p>
        }
    </div>

And here is the CreateOrder view:

@model MasterDetailBindingProblem.Models.MyViewModel
@{
    ViewBag.Title = "OrdersCreate";
}

<h2>OrdersCreate</h2>
<script src="../../Scripts/jquery-1.8.2.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.8.2.min.js" type="text/javascript"></script>

<script type="text/javascript">
    $(document).ready(function () {
        //Dropdownlist Selectedchange event
        //$("#LoadingConfig").change(function () {
        $("#FooDDL").change(function () {
            $("#FooDiv").empty();   //Here is where we will clear out the Div/list/For the JointLayers.
            $("#FooDiv").load("/orders/getitemspartial", { id: $("#FooDDL").val() });
@*            $.ajax({
                type: 'POST',
                url: '@Url.Action("GetJointLayersPartial")', // we are calling json method
                dataType: 'json',
                data: { id: $("#LoadingConfig").val() },
                success: function (layerCount) {
                    // states contains the JSON formatted list
                    // of states passed from the controller
                    alert(layerCount);
                    //20171030 EAS - Below code is the loop through the json data code.
                    //$.each(joiningMechanisms, function (i, joiningMechanism) {
                        //$("#JoiningMechanism").append('<option value="' + joiningMechanism.Value + '">' + joiningMechanism.Text + '</option>');
                    //}); // here we are adding option for States
                },
                error: function (ex) {
                    alert('Failed to retrieve foo data.' + ex);
                    console.log('Failed to retrieve foo data.' + ex);
                }
            });*@
            return false;
        })
        $(".partialTest").click(function (evt) {
            var cell = $(evt.target).closest("tr").children().first();
            var custID = cell.text();
            $("#testpartial").load("/workorders/getview");
        });
    });
</script>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Create Order</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Order_Info.OrderID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Order_Info.OrderID)
            @Html.ValidationMessageFor(model => model.Order_Info.OrderID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Order_Info.CustomerName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Order_Info.CustomerName)
            @Html.ValidationMessageFor(model => model.Order_Info.CustomerName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Order_Info.OrderDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Order_Info.OrderDate)
            @Html.ValidationMessageFor(model => model.Order_Info.OrderDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Order_Info.RequiredBy)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Order_Info.RequiredBy)
            @Html.ValidationMessageFor(model => model.Order_Info.RequiredBy)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.SelectedRowCount)
        </div>
        <div class="editor-field">
            @*@Html.DropDownListFor(model => model.SelectedRowCount, new SelectList(ViewBag.JointTypesDDL, "Value", "Text"),"<Select Joint Type>", new { @class = "dropdown1" })*@
            @Html.DropDownList("FooDDL", new List<SelectListItem>
                {
                    new SelectListItem{ Text="Option 1", Value = "1" },
                    new SelectListItem{ Text="Option 2", Value = "2" },
                    new SelectListItem{ Text="Option 3", Value = "3" },
                })
            @Html.ValidationMessageFor(model => model.SelectedRowCount)
        </div>

        <div class="editor-label">
            @*@Html.LabelFor(model => model.LCTID)*@
        </div>
        <div class="editor-field">            
            @*
            //Below gives following error:
            //The ViewData item that has the key 'LCTID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
            *@
            @*@Html.DropDownListFor(m => m.LCTID, ViewBag.LoadingConfigurationsDDL as List<SelectListItem>, new { style = "width:250px", @class = "dropdown1" })*@

@*            @Html.DropDownListFor(m => m.LCTID, new SelectList(Model.LoadingConfigs, "Value", "Text"),"<Select Loading Config>", new { @class = "dropdown1" })
            @Html.ValidationMessageFor(model => model.LCTID)*@

            <div id="FooDiv">
                @Html.Partial("_OrderDetailsPartial", Model.OrderItems)
            </div>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "OrdersIndex")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Questions I Have Been Asked Alot

  • Where's the database? I didn't include a database because you really don't need one to see where the code is broken. It breaks right before the data would get saved.
  • What's the input that causes the problem? It seems not to matter.
  • What Environment was this done in? I am using Visual Studio 2012 and MVC4
  • Why aren't you passing the whole model to the Ajax call? Two reasons. One, it seemed to intuitively defeat the purpose of using a partial view. Two, several examples didn't do it.

Summary: I have looked at a ton of examples over the last 3 weeks. Some here, some elsewhere. I may well have missed the perfect example. If I did, I am sorry. From what I learned from all of them, I think the problem lies with how I am generating the HTML in the partial view. Something about that markup is preventing the binder from working. But I could be way off.

What I am hoping is that someone can take this and tell me what I am doing wrong and SHOW me what I need to do to fix it. Thank you for taking the time to read this far. If you need more info, I will post it as soon as I can.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.312023 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO