16

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

4 Answers 4

12

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" />
Sign up to request clarification or add additional context in comments.

5 Comments

Very close, but the accepted answer in this url has a more complete solution: stackoverflow.com/questions/8598214/…
@Levitikon - The accepted solution in your link is overkill. You don't need to specify a .Index for every item. I've used the above approach numerous times without needing the extra hidden inputs described in your link. Plus, Phil Haack was on the ASP.NET MVC development team, so I'm pretty sure what he wrote in his blog is the way to go.
@amurra in the Haack article he says to use a separate hidden input for each field so it would seem that it's necessary.
I can confirm that in MVC 5.2.3 the hidden input is necessary for each field and value needs to be the correct index.
That blog post didn't do a great job explaining what the .Index suffix meant. At first, I was confused about how cold and caliente could be indices into a list, then I realized it was supposed to be declaring the "next index identifier." It's basically telling the model binder that the next index succeeding the previous one should be named "cold." It's really weird that the author chose this example.
0

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

Comments

0

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.

1 Comment

Yes and no. It appears MVC stops building the array when there is a gap in the sequence.
-1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.