Binding arrays with missing elements in asp.net mvc

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" }
     }
}

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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" />

Method 2

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

Method 3

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.

Method 4

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


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x