14

I would like to assign a static list of items in a SelectList() to a Html.DropDownList() in ASP.NET MVC, what is the best practice?

I was about to try to find a way to use new SelectList(new {key = "value"}... but one, that didn't work, and two, would I be breaking a law here, should my static list be declared in ViewData anyway and passed as IList/IENumerable?

GONeale
  • 26,302
  • 21
  • 106
  • 149

3 Answers3

23

It is a best practice not to create the SelectList in the view. You should create it in the controller and pass it using the ViewData.

Example:

  var list = new SelectList(new []
                                          {
                                              new {ID="1",Name="name1"},
                                              new{ID="2",Name="name2"},
                                              new{ID="3",Name="name3"},
                                          },
                            "ID","Name",1);
            ViewData["list"]=list;
            return View();

you pass to the constructor: the IEnumerable object, the value field the text field and the selected value.

in the View:

 <%=Html.DropDownList("list",ViewData["list"] as SelectList) %>
iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • agreed with Marwan. Create a SelectList object and pass that to your View. Personally, I would have strongly-typed views, but that is out of the scope of this question. – Pure.Krome May 16 '09 at 14:22
  • I use a strongly-typed view, why would you define the SelectList in the view? It's a 'view-particular' thing? I think it would be better to simply expose the data, that is what you are meant to give a view, data not context and data.? – GONeale May 18 '09 at 23:49
  • Actually I think it is very wrong, SelectList is used for rendering a dropdownlist which is totally UI specific. What if I now wanted to use this data for a check box list or something which doesn't support SelectList? – GONeale May 18 '09 at 23:52
  • I think it is better to define your SelectList in the controller to keep your view clean and contain as less code as possible. If you check the Nerd Dinner application, you will see it did the same thing. –  May 19 '09 at 07:36
  • OK Marwan, I still am meaning to check that out. However I want to make sure I don't just favour cleanness over correctness. – GONeale May 21 '09 at 01:06
13

All MVC noobs initially avoid the 'M' word, but it does kinda start at the beginning of the MVC acronym. So maybe, just maybe, you might want to start your solution with a Model... just saying.

Do not Repeat Yourself (DRY). You're going to end up copying and pasting the "new PageData()" to each and every Controller that passes the option list to a View. Then you're going to want to delete or add an option and have to edit every controller's PageData.

Moreover, you want to type the least amount of code with the fewest unnecessarily verbose "add"s, "new"s, "IS"s, and "Name"s. Because you only have a key-value pair in the select option (and/or a radio button list), use the lightest data structure possible, i.e. a Dictionary --in your Model.

Then simply reference the Model in the Controller and convert the Dictionary to a SelectList in the View using a DropDownListFor containing a LINQ Lambda expression.

Intimidated? You're not alone. I certainly was. Here's an example I used to teach myself the M, the V, and the C:

The Model part of MVC:

using System.Web.Security;
using System.Collections.Generic;
using System.Text;
using System.Linq.Expressions;
using System.Web.Routing;
using System.Web.Helpers;
using System.Web.Mvc.Html;
using MvcHtmlHelpers;
using System.Linq;

// EzPL8.com is the company I work for, hence the namespace root. 
    // EzPL8 increases the brainwidths of waiters and bartenders by augmenting their "memories" with the identifies of customers by name and their food and drink preferences.
   // This is pedagogical example of generating a select option display for a customer's egg preference.  

namespace EzPL8.Models     
{
    public class MyEggs    
    {
        public Dictionary<int, string> Egg { get; set; }

        public MyEggs()  //constructor
        {
            Egg = new Dictionary<int, string>()
            {
                { 0, "No Preference"},  //show the complete egg menu to customers
                { 1, "I hate eggs"},    //Either offer an alternative to eggs or don't show eggs on a customer's personalized dynamically generated menu

                //confirm with the customer if they want their eggs cooked their usual preferred way, i.e.
                { 2, "Over Easy"},  
                { 3, "Sunny Side Up"},
                { 4, "Scrambled"},
                { 5, "Hard Boiled"},
                { 6, "Eggs Benedict"}
            };
    }
}

The Controller is now fairly simple, just passing the model. It avoids creating an isolated concept, which probably isn't isolated to just one page.:

public ActionResult Index()
{
   var model = new EzPL8.Models.MyEggs();
   return View(model);
}

The View uses DropDownListFor (instead of DropDownList) and a Lambda expression for strong typing in the event refactoring is required:

@Html.DropDownListFor(m => m.Egg, new SelectList( Model.Egg, "Key", "Value"))

Voilà, the resulting HTML:

<select id="Egg" name="Egg">
<option value="0">No Preference</option>
<option value="1">I hate eggs</option>
<option value="2">Over Easy</option>
<option value="3">Sunny Side Up</option>
<option value="4">Scrambled</option>
<option value="5">Hard Boiled</option>
<option value="6">Eggs Benedict</option>
</select>

Note: don't get confused by the VALUE in <option value="6">, which is the Key from the dictionary, from the "Value" in the SelectList(), which is the text/title (e.g. Eggs Benedict) that ends up between the option tags.

Use Case: To minimize traffic between the application and the database I created a static list to avoid a database query for the drop down list that rarely, if ever changes. However, change is inevitable and six-months from now a diner at my customer's restaurant dies; not because of the green ham, but because they were allergic to eggs and the cook mixed some in with their waffles.

The restaurant needs their customer info updated to include food allergies immediately. While they love repeat customers, they're not cool with dead customers coming back as zombies that have no way to pay because their credit cards were cancelled.

Rhetorical Question: Should I modify all the controllers and views related to customer egg preferences? Or simply insert { 7, "Allergic to Eggs"} into the model?

Another Rhetorical Question: Aren't omelettes eggs? Do you want to add {8, "Omelette, Western"}, {9, "Omelette, Mushroom-Feta-Spinach"} once to the model and have the new additions automagically propagate throughout all dropdown lists in all the views that use them?

Bottom line(s) This is probably way more than you asked for, ...but you did say MVC, not just VC:
1. Use a Model in *M*VC. 2. Use a dictionary in the Model when a select list is based on nothing more than a "primary key" and a title. 3. If your static list doesn't jive with a database Lookup table somewhere, your app probably isn't very useful. When you add an option to a static list, more than likely you'll also need to perform an insert to the Lookup table to avoid a primary key/foreign key relationship integrity error with other tables in the database. 4. Use lambda and strongly typed data structures to avoid errors and get typeahead support.

Jules Bartow
  • 956
  • 14
  • 14
3

OK I decided to take my own advice, and this should be defined in the controller:

FYI, I just returned:

PageData data = new PageData()
           {
               Formats = new[]
                             {
                                 new { ID = "string", Name = "Text" },
                                 new { ID = "int", Name = "Numeric" },
                                 new { ID = "decimal", Name = "Decimal" },
                                 new { ID = "datetime", Name = "Date/Time" },
                                 new { ID = "timespan", Name = "Stopwatch" }
                             },
               .............

           };
return View(data);

... (ignore context) and in the View ASPX side:

<%= Html.DropDownList("type.field", new SelectList(ViewData.Model.Formats, "ID", "Name"...

If anyone has a more optimal way of doing this I'll be happy to accept their answer.

GONeale
  • 26,302
  • 21
  • 106
  • 149
  • I have selected my answer as it is the one I went with. – GONeale May 21 '09 at 01:07
  • This works fine, but if i want dropdown to load Decimal as a startup value, what should i do ? – Sakthivel Jun 07 '13 at 07:27
  • Defining it in the controller is better than the view, but it should really be in the model as @JulesBartow suggests. What if you want to reuse the drop-down elsewhere? Cut and paste is not the right answer. – Louise Eggleton Mar 07 '19 at 16:29