0

Consider these two view models

public class PersonViewModel
{
    public int PersonId { get; set; }
    public string PersonName { get; set; }
    public int PersonAge {get; set;}

    public virtual PersonJobSplitViewModel JobSplit { get; set; } // hold each split
    public virtual List<PersonJobSplitViewModel> JobSplits { get; set; } //contain all splits
}

public class PersonJobSplitViewModel
{
    public int PersonJobSplitId { get; set; }
    public int PersonId { get; set; }
    public string JobRole { get; set; }
    public decimal SplitPercentage { get; set; }
    public virtual PersonViewModel PersonViewModel { get; set; }
}

Each person can have between 1 - 3 jobs.

I have a Create view which is bound to my PersonViewModel and in my controller's GET method I am creating an instance of List<PersonJobSplitViewModel> with a capacity of 3 and assigning it to PersonViewModel.JobSplits

@model MySolution.Web.ViewModels.PersonViewModel

...

@for (var i = 0; i < Model.JobSplits.Capacity; i++)
{
    @Html.EditorFor(model => model.JobSplit.JobRole);
    @Html.EditorFor(model => model.JobSplit.SplitPercentage);
}

This results in the role and percentage inputs being rendered to the view 3 times. My POST method is expecting a PersonViewModel however PersonViewModel.JobSplits is coming in as null. The JobSplit property contains one of my 3 splits, as I kind of expect.

So how do I post the model bound with it's full list of JobSplits through to the controller?

I've found similar things have been answered previously but I cant seem to find a straight forward solution that is relevant to MVC5 and relates to tagging a list to a large model as apposed to just passing a list to the controller.

Update

I've now tried doing the following

@for (var i = 0; i < Model.JobSplits.Capacity; i++)
{
    @Html.EditorFor(model => model.JobSplits[i].JobRole);
    @Html.EditorFor(model => model.JobSplits[i].SplitPercentage);
}

But I get:

Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index

JsonStatham
  • 8,163
  • 26
  • 88
  • 160

1 Answers1

0

Worked this out straight after posting.

Controller

//Prep view Model
PersonViewModel viewModel = new PersonViewModel();
List<PersonJobSplitViewModel> instantiatedSplitCount = new List<PersonJobSplitViewModel>(3);

for (int i = 0; i < 3; i++)
{
    instantiatedSplitCount.Add(null);
}

viewModel.JobSplits = instantiatedSplitCount;
return View(viewModel);

View

@for (var i = 0; i < Model.JobSplits.Count(); i++)
{
    @Html.EditorFor(model => model.JobSplits[i].JobRole);
    @Html.EditorFor(model => model.JobSplits[i].SplitPercentage);
}

It seems doing a check on capacity was the problem, assigning 3 nulls to the list before I pass through, and then checking the count was the solution.

Not pretty in my Controller but if anyone else can suggest a more syntactic solution please share.

JsonStatham
  • 8,163
  • 26
  • 88
  • 160
  • You have indicated _Each person can have between 1 - 3 jobs._ so if any of your properties have validation attributes, this will fail. You really should consider dynamically adding the `JobSplits` (a few examples [here](http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796#29161796), [here](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) and [here](http://stackoverflow.com/questions/29837547/set-class-validation-for-dynamic-textbox-in-a-table/29838689#29838689) –  Jan 28 '16 at 22:37
  • Also unclear why you have both `PersonJobSplitViewModel JobSplit` and `List JobSplits` in your model. –  Jan 28 '16 at 22:39