Search code examples
asp.netasp.net-mvcasp.net-mvc-viewmodel

Partial viewmodel is null when posting to controller


I've got a viewmodel that contains other viewmodels.

public class AggregateVM
{
    public BrandVM BrandVM { get; set; }
    public TinTypeVM TinTypeVM { get; set; }
}

When I http post to the controller action, the TinTypeVM is populated with my edited values, but the BrandVM viewmodel where I used a partial is always null.

Here's are the view.

@model SaveEF.ViewModels.AggregateVM
@using (Html.BeginForm("EditAggregate", "Aggregate")) 
{
    @Html.Partial("_EditBrand", Model.BrandVM)

    @Html.Label("Tin Name")
    @Html.EditorFor(model => model.TinTypeVM.Name)
    <input type="submit" value="Save" />
}

Here's the partial view.

@model SaveEF.ViewModels.BrandVM
@Html.Label("Brand Name")
@Html.EditorFor(model => model.Name)

Here's the controller action.

public ActionResult EditAggregate(AggregateVM vm)
{
    SaveBrand(vm.BrandVM);
    SaveTinType(vm.TinTypeVM);
    return RedirectToAction("Index");
}

How can I use partials in the view and still pass a single view model into the EditAggregate action? I've tried different parameters in Html.BeginForm("EditAggregate", "Aggregate", FormMethod.Post, new { vm = Model })) but that didn't help.


Solution

  • Short Answer

    You need to pass AggregateVM to your partial too.

    Long Answer

    The way you are doing it right now is:

    @model SaveEF.ViewModels.BrandVM
    @Html.EditorFor(model => model.Name)
    

    So if you were to inspect the name generated for the editor would be Name. Thus when you post, the default model binder of MVC will look for a matching property in your view model. But you view model is like this:

    public class AggregateVM
    {
        public BrandVM BrandVM { get; set; }
        public TinTypeVM TinTypeVM { get; set; }
    }
    

    And there is no Name property.

    Fix

    You need to pass AggregateVM to your partial too.

    @model SaveEF.ViewModels.AggregateVM
    @Html.EditorFor(model => model.BrandVM.Name)
    

    and now the name for the editor will be BrandVM.Name. So when you post, the default model binder will look for the property BrandVM.Name and find it. Thus it will populate it.

    The other alternative is to specify the name attribute yourself by using @Html.Editor or pass the attribute.