C Bauer
C Bauer

Reputation: 5103

ViewModel inside of ViewModel - How to get it to post?

I have a viewmodel inside of another viewmodel for seperation of concerns. I created an editor template for it and set up the default values in the controller at runtime. Unfortunately when the parent view model posts to the controller, it does not save the values of the child view models' items. Here is the code:

Note: Some code names were changed, so if there's any incongruities please point it out in the comment. I've gone over it about 4x and found them all I think.

public class ParentViewModel {
    public ChildViewModel {get;set;}
}
public class ChildViewModel {
    public List<Item> Items {get;set;}
}
public class Item {
    public int Id {get;set;
    public string Name {get;set;}
}

I've created an EditorTemplate that binds properly on the view

@model MyProject.ViewModels.ChildViewModel

@foreach (var item in Model.Items)
{
    <div class="Item" @String.Format("id=Item{0}", @item.Id) >
        Item #@Html.DisplayFor(models => item.Id): 
        @Html.LabelFor(model => item.Name)
        @Html.EditorFor(model => item.Name)
    </div>   
}

However, when I submit the form that the ParentViewModel is bound to, the ChildViewModel's items are null!

Controller.cs

public class ControllerController{
    public ActionResult Form {
        return View(new ParentViewModel {
            ChildViewModel = new ChildViewModel {
                Items = new List<Item>(Enumerable.Range(1,20).Select(i => new Item { Id=i })
            }
        });
    }
    [HttpPost]
    [ActionName("Form")]
    public class ActionResult FormSubmitted(ParentViewModel parentViewModel) {
        //parentViewModel.ChildViewModel.Items is null!
        _fieldThatIsRepresentingMyDataService.Save(parentViewModel);
    }
}

ViewView.cshtml

 <div class="editor-label">
     @Html.LabelFor(model => model.ChildViewModel)
</div>
<div id="ItemList" class="editor-field">
    @Html.EditorFor(model => model.ChildViewModel)
</div>

Any help is greatly appreciated.

Upvotes: 0

Views: 2452

Answers (1)

Mrchief
Mrchief

Reputation: 76238

The problem is not with nested viewmodels, but the way model binding works with forms and arrays.

You need to ensure that your form items render like this:

<input type="text" name="people[0].FirstName" value="George" />
<input type="text" name="people[0].LastName" value="Washington" />
<input type="text" name="people[1].FirstName" value="Abraham" />
<input type="text" name="people[1].LastName" value="Lincoln" />
<input type="text" name="people[3].FirstName" value="Thomas" />
<input type="text" name="people[3].LastName" value="Jefferson" />

The key part is the array index in input's name attribute. Without the index part, Model Binding will not populate your List.

And to render that, you need a for loop:

@for (int i = 0; i < Model.Items.Length; i++) {
 ...
  @Html.EditorFor(m => Model.Items[i].Name)
 ...
}

Check out this post from Phil Haack that talks about the it in details.

Upvotes: 2

Related Questions