0

The default model binder is not mapping the collections in my model. Here is my code:

Model:

public class Company
{
   public string Name;
   public List<CompanyActivity> Activities
}

public class CompanyActivity
{
   public string  Code;
   public string Description
}

Controller:

[HttpPost]
public ActionResult Index(Company company) {}

View/HTML:

<input name="Name" type="text" value="some name" />
<input name="Activities[0].Code" type="text" value="1" />
<input name="Activities[0].Description" type="text" value="a" />
<input name="Activities[1].Code" type="text" value="2" />
<input name="Activities[1].Description" type="text" value="b" />

The Name input is mapped but the Activities list is empty.

jlp
  • 7,992
  • 15
  • 47
  • 70
  • The HTML you've given shows the `value=""` attribute as empty. What happens when you provide values? Also, what is your MVC view source? – Dai Jan 16 '14 at 08:14

3 Answers3

1

You have defined your model with fields, but you have to use properties. You only have to change your model for this:

public class Company
{
    public string Name { get; set; }
    public List<CompanyActivity> Activities { get; set; }
}

public class CompanyActivity
{
    public string Code { get; set; }
    public string Description { get; set; }
}

Differences between fields and properties: Difference between Property and Field in C# 3.0+ and ASP.net MVC - Model binding excludes class fields?

Community
  • 1
  • 1
  • why? I have created a proyect with your code. Without properties dont bind and with properties bind properly. The HTML is right, like I said I have only added the {get;set;} and it works fine. – Carlos Corral Carvajal Jan 16 '14 at 10:14
  • Interesting I didn't realize the modelbinder was smart enough to do this using that name syntax. My answer is not nearly as neat in comparison. – Oliver Jan 16 '14 at 12:17
0

The names need to be identical for it to build them as a collection/array:

I would create a ViewModel to handle this case, this is why I almost always build a ViewModel to handle view rendering and the idiosyncrasies of MVC.

Model

public class Company
{
   public string Name;
   public List<CompanyActivity> Activities
}

ViewModel

public class CompanyViewModel
{
   public string Name;

   //View specific
   public List<int> CompanyActivityCodes
}

View

<input name="Name" type="text" value="" />
<input name="CompanyActivityCodes" type="text" value="" />
<input name="CompanyActivityCodes" type="text" value="" />

This will then bind the CompanyActivityCodes property, you can then reassign these to build up the Activities property in the controller.

public ActionResult Index(CompanyViewModel companyViewModel) 
{
  var company = new Company { Name = companyViewModel }; 
  company.Activities = companyViewModel.CompanyActivityCodes.Select(x =>  new CompanyActivity { Code = x });
}
Oliver
  • 30,713
  • 10
  • 54
  • 69
  • I have updated my code. Activities is a list of objects, not a list of ints. – jlp Jan 16 '14 at 08:50
  • yes but it's not that clever where it can figure out the nested properties when using collections. – Oliver Jan 16 '14 at 12:11
  • I understand, the modelbinder is not advanced enough however to figure out the nested properties when working with collections. For this reason I created a ViewModel which essentially "flattens" you CompanyActivity class into a flat class. As you see I have named the property CompanyActivityCodes as it flattens the code property in the activities collection. You've edited the question to include additional properties which complicates matters. – Oliver Jan 16 '14 at 12:14
0

it shoould be

<input name="company.Activities[0].Code" type="text" value="" />
Vova Bilyachat
  • 15,962
  • 4
  • 48
  • 72