Reputation: 14210
I am trying to bind a dynamic array of elements to a view model where there might be missing indexes in the html
e.g. with the view model
class FooViewModel
{
public List<BarViewModel> Bars { get; set; }
}
class BarViewModel
{
public string Something { get; set; }
}
and the html
<input type="text" name="Bars[1].Something" value="a" />
<input type="text" name="Bars[3].Something" value="b" />
<input type="text" name="Bars[6].Something" value="c" />
at the moment, bars will just be null. how could I get the model binder to ignore any missing elements? i.e. the above would bind to:
FooViewModel
{
Bars
{
BarViewModel { Something = "a" },
BarViewModel { Something = "b" },
BarViewModel { Something = "c" }
}
}
Upvotes: 16
Views: 6355
Reputation: 21
MVC is able to populate list itself.
public ActionResult Index(FooViewModel model)
{
...
So no matter if anything is missing mvc will create new List<BarViewModel>
and
for each found index - [1],[3],[6] it will create new BarViewModel
and add it to List. So you will get FooViewModel with populated Bars.
Upvotes: 0
Reputation: 15401
Add the .Index
as your first hidden input to deal with out of sequence elements as explained in this Phil Haacked blog post:
<input type="text" name="Bars.Index" value="" />
<input type="text" name="Bars[1].Something" value="a" />
<input type="text" name="Bars[3].Something" value="b" />
<input type="text" name="Bars[6].Something" value="c" />
Upvotes: 12
Reputation: 2336
A possible workaround could be to instantiate the ViewModel and the collection to the correct size (assuming it's known), then update it with TryUpdateModel... something like:
[HttpPost]
public ActionResult SomePostBack(FormCollection form)
{
// you could either look in the formcollection to get this, or retrieve it from the users' settings etc.
int collectionSize = 6;
FooViewModel bars = new FooViewModel();
bars.Bars = new List<BarViewModel>(collectionSize);
TryUpdateModel(bars, form.ToValueProvider());
return View(bars);
}H
Upvotes: 0
Reputation: 37378
i didnt know even that worked!
bearing that in mind, id have done something like:
<input type="text" name="Bars.Something" value="a" />
<input type="hidden" name="Bars.Something" value="" />
<input type="text" name="Bars.Something" value="b" />
<input type="hidden" name="Bars.Something" value="" />
<input type="hidden" name="Bars.Something" value="" />
<input type="text" name="Bars.Something" value="c" />
which would hopefully post
a,,b,,,c
but I suspect that will bind in the same way as you describe
Youre probably going to have write a custom model binder that looks for the max index, makes a list of that size then puts the elements in the correct place.
Saying all that, wait for someone else to post a really simple attribute you can put on your property that makes it just work ;D
Upvotes: -1