Search code examples
c#asp.net-mvc-2model-bindingmodel-view

Model binding for a ViewModel containing multiple objects


I have a strongly typed view of type ProductListingViewModel which in turn contains a ProductViewModel. (both custom view models).

I have some form elements on my page and these are created like so:

<%: Html.DropDownListFor(m => m.ProductViewModel.CategoryId, Model.Categories)%>

which generates the HTML:

<select name="ProductViewModel.CategoryId" id="CategoryId">

With the default model binding I expected that when I post to my controller action which accepts a parameter of type ProductListingViewModel, that it'd know to populate the ProductViewModel.CategoryId with the relevant data.

The name of the select list seems to indicate that it knows there's a ProductViewModel with a CategoryId property however when I post to my controller method, the ProductViewModel is null. If I create this during construction of the ProductListingViewModel then it's no longer null but the default binder doesn't seem to be populating the properties as I expected.

Is this a case for a custom model binder or am I just missing something fundamental?

Cheers.


Solution

  • Let me try to summarize (correct me if I am wrong).

    Model:

    public class ProductListingViewModel
    {
        public ProductViewModel ProductViewModel { get; set; }
        public IEnumerable<SelectListItem> Categories { get; set; }
    }
    
    public class ProductViewModel
    {
        public string CategoryId { get; set; }
    }
    

    Controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new ProductListingViewModel
            {
                Categories = new SelectList(new[]
                {
                    new { Value = "1", Text = "category 1" },
                    new { Value = "2", Text = "category 2" }
                }, "Value", "Text")
            };
            return View(model);
        }
    
        [HttpPost]
        public ActionResult Index(ProductListingViewModel model)
        {
            return View(model);
        }
    }
    

    View:

    <% using (Html.BeginForm()) { %>
        <%: Html.DropDownListFor(m => m.ProductViewModel.CategoryId, Model.Categories)%>
        <input type="submit" value="OK" />
    <% } %>
    

    Now when you submit the form you will get:

    model.ProductViewModel.CategoryId = the id that was selected in the drop down list
    

    Isn't what you are after?