Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to a SelectList in MVC

Once again I'm confronted with a "This shouldn't be this ?*!# hard" situation.

Problem: I want to use a form in MVC for creation of an object. One of the elements of the object is a set of limited choices - a perfect candidate for a drop down list.

But if I use a SelectList in my model, and a drop down list in my View, and then try to post the Model back to my Create method, I get the error "Missing Method Exception:No Parameterless constructor for this object". Exploring the MVC source code, it appears that in order to bind to a model, the Binder has to be able to create it first, and it can't create a SelectList because there is no default constructor for it.

Here's the simplified code: For the model:

public class DemoCreateViewModel
{
    public SelectList Choice { get; set; }
}

For the controller:

//
// GET: /Demo/Create

public ActionResult Create()
{
    DemoCreateViewModel data = new DemoCreateViewModel();
    data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
    ViewData.Model = data;
    return View();
}

//
// POST: /Demo/Create

[HttpPost]
public ActionResult Create(DemoCreateViewModel form)
{
    try
    {
        // TODO: Add insert logic here

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

And for the View:

<fieldset>
    <legend>Fields</legend>
    <%= Html.LabelFor(model => model.Choice) %>
    <%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>

Now, I know I can MAKE this work by dropping back 10 yards and punting: bypass model binding and drop back to the FormCollection and validate and bind all the fields myself, but there's got to be a simpler way. I mean, this is about as simple a requirement as it gets. Is there a way to make this work within the MVC ModelBinding architecture? If so, what is it? And if not, how come?

Edit: Well, I have egg on my face, but maybe this will help someone else. I did some more experimenting and found a simple solution that seems to work.

Provide a simple value (string or integer, depending on what your select list value type is), and name that as the model element that you bind to. Then provide a second element as the select list of choices, and name it something else. So my model became:

public class DemoCreateViewModel
{
    public string Choice { get; set; }
    public SelectList Choices { get; set; }
}

And then the DropDownListFor statement in the View becomes:

<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>

When I do this, the submit button correctly binds the choice made in the form to the string Choice, and submits the model back to the second Create method.

like image 579
Dave Hanna Avatar asked May 26 '10 22:05

Dave Hanna


People also ask

How do I bind a DropDownList in MVC model?

Binding MVC DropDownList with Static Values Just add an Html helper for DropDownList and provide a static list of SelectListItem. The values added as SelectListItem will be added and displayed in the DropDownList. In this way, you do not need to add anything to Controller Action.


1 Answers

Here is one approach:

@Html.DropDownListFor(model => model.Choice, 
                      ViewBag.Choices as SelectList, 
                      "-- Select an option--",
                      new { @class = "editor-textbox" })

Notice that I use ViewBag to contain my SelectList. This way when you post back, the client doesn't send the entire select list up to the server as part of the model.

In your controller code, you just need to set the view bag:

ViewBag.Choices = new SelectList(....
like image 98
Geoff Cox Avatar answered Oct 05 '22 08:10

Geoff Cox