Question

I'm seeing an odd (to me) issue where I am attempting to post an array of integers based on checkboxes. When the values are not posted in order, the model binder doesn't seem to function in a way that I would expect.

I can reproduce this against a very simple Action

    public ActionResult Debug(string[] Unassigned)
    {
        return RedirectToAction("Index", new { id = 7 });
    }

An example of this not working would be when the following values are posted (copied in via the immediate window). I would expect Unassigned to have the values 8 and 6.

? Request.Form.AllKeys
{string[4]}
[0]: "__RequestVerificationToken"
[1]: "LoginId"
[2]: "Unassigned[1]"
[3]: "Unassigned[3]"
? Request.Form["Unassigned[1]"]
"8"
? Request.Form["Unassigned[3]"]
"6"
? Unassigned
null

When the values are passed in order, this works (note Unassigned[3] doesn't get bound as Unassigned[2] is not posted.

? Request.Form.AllKeys
{string[5]}
[0]: "__RequestVerificationToken"
[1]: "LoginId"
[2]: "Unassigned[0]"
[3]: "Unassigned[1]"
[4]: "Unassigned[3]"
? Request.Form["Unassigned[0]"]
"2"
? Request.Form["Unassigned[1]"]
"8"
? Request.Form["Unassigned[3]"]
"6"
? Unassigned
{string[2]}
[0]: "2"
[1]: "8"

Minus the formatting, my HTML looks like this

<input type="checkbox" name="Unassigned[0]" value="2">
<input type="checkbox" name="Unassigned[1]" value="8">
<input type="checkbox" name="Unassigned[2]" value="7">
<input type="checkbox" name="Unassigned[3]" value="6">
<input type="checkbox" name="Unassigned[4]" value="5">
<input type="checkbox" name="Unassigned[5]" value="9">
<input type="checkbox" name="Unassigned[6]" value="4">
<input type="checkbox" name="Unassigned[7]" value="3">
<input type="checkbox" name="Unassigned[8]" value="1">

Given that select is not broken, what am doing wrong?

Était-ce utile?

La solution

Given that select is not broken, what am doing wrong?

You are not respecting the convention for binding to a list because you have holes in your indexes. You could use non-sequential indexes, like Guids. Look at Phil Haacks blog post I have linked to. He has an entire section dedicated to it.

Other possibility that I would recommend you is to use a view model. So go ahead and write one:

public class ItemViewModel
{
    public string Id { get; set; }
    public bool Selected { get; set; }
}

and then you could have a controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new[] 
        {
            new ItemViewModel { Id = "2" },
            new ItemViewModel { Id = "8" },
            new ItemViewModel { Id = "7" },
            new ItemViewModel { Id = "6" },
            new ItemViewModel { Id = "5" },
            new ItemViewModel { Id = "4" },
            new ItemViewModel { Id = "3" },
            new ItemViewModel { Id = "1" },
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(ItemViewModel[] model)
    {
        // everything will be correctly bound here
    }
}

and a corresponding strongly typed view:

@model ItemViewModel[]
@using (Html.BeginForm())
{
    for (var i = 0; i < Model.Length; i++)
    {
        Html.HiddenFor(x => x[i].Id)
        Html.CheckBoxFor(x => x[i].Selected)
    }

    <button type="submit">OK</button>
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top