-2

This has been brought up here numerous times but I can't find a solution that fits.

I'm trying to populate a DDL in an MVC view without using Viewbag.

This is the controller code:

public ActionResult Name()
{
     ***
     List<SelectListItem> Items = Model.GetItemList();
     return View("Name");
}

However, I can't find the View syntax that works. I know that the List I'm getting back from the model contains the correct data. In the ‪view I have this which is not correct. How do I access my "Items" list in the view? The view is typed obviously.

 @Html.DropDownListFor(n => n.Value,  new SelectList(Items, "Value", "Text"))
Borges
  • 17
  • 6
  • 1
    Do you use ViewModels? Have you tried to use viewmodel with your list as a part of it? – Igor Yalovoy Apr 06 '18 at 20:32
  • return View("Name"); looks wrong. Try return View(Model); and then you should have access to Model.GetItemList() in the view – Broom Apr 06 '18 at 20:34
  • What model is your view expecting? – maccettura Apr 06 '18 at 20:35
  • Let's say the name of the model is ModelName? – Borges Apr 06 '18 at 20:38
  • Perhaps the problem is that the Items list is a local variable and is not tied to an object in the model? – Borges Apr 06 '18 at 20:38
  • @Borges the problem is you never pass a model to your view. You need to pass an instance in the `View()` call. So something like: `return View(someModelNameInstance);`. Also, even if you had no model, calling `View("Name")` is redundant, if the action name matches the view just return `View();` – maccettura Apr 06 '18 at 20:39
  • So what would the DropdownListFor syntax be? That was my original question. :) – Borges Apr 06 '18 at 21:06
  • You need to pass a model to the view. That model will contain a property `int Value` to bind to, and a `IEnumerable` property for the options. Refer [this answer](https://stackoverflow.com/questions/34366305/the-viewdata-item-that-has-the-key-xxx-is-of-type-system-int32-but-must-be-o) for a typical example –  Apr 06 '18 at 22:09

2 Answers2

2

Code is not tested, so might have some typos, but it should give you a general idea.

If you use ViewModel which would have property called Items then you have to pass it to the view and use it as:

public class MyViewModel
{
    string SelectedItem { get; set; }
    List<SelectListItem> Items { get; set; }
}

And then

public ActionResult Name()
{
     ***
     List<SelectListItem> Items = Model.GetItemList();
     var myViewModel = new MyViewModel { Items = Items }
     return View("Name", myViewModel);
}

And then in cshtml:

// at the start of the file
@model yournamespace.MyViewModel

// somewhere below
@Html.DropDownListFor(n => n.SelectedItem, Model.Items);
Igor Yalovoy
  • 1,715
  • 1
  • 17
  • 22
  • So the model for the view is `List`? According to OP that is not true. Your code won't help fix the problem – maccettura Apr 06 '18 at 20:40
  • Is that Model in the view supposed to be the actual name of the model? Or the name of the object instantiated in the controller? – Borges Apr 06 '18 at 20:43
  • @Borges Model is always the name of your ViewModel in the view, doesn't matter which ViewModel class you use. Model is property of [WebViewPage](https://msdn.microsoft.com/en-us/library/system.web.mvc.webviewpage.model(v=vs.118).aspx) class. – Igor Yalovoy Apr 06 '18 at 20:45
  • Does it have to be typecast? Because it does not work as is. – Borges Apr 06 '18 at 20:51
  • you have to specify your model type in your cshtml file, like this `@model yournamespace.MyViewModel` at the start of the file. – Igor Yalovoy Apr 06 '18 at 20:54
  • I did. It's a typed view. It's saying that it cannot convert that model type to an iEnumerable. – Borges Apr 06 '18 at 20:55
  • And what is your model type? And what do you pass to DropDownListFor? – Igor Yalovoy Apr 06 '18 at 20:56
  • Is MyViewModel supposed to be the name of the model? The one I instantiate? If so then why declare it twice? – Borges Apr 06 '18 at 21:06
  • No, you can have any name for your Model variable at controller. You just have to pass it to the view and declare it there. – Igor Yalovoy Apr 06 '18 at 21:10
  • Sorry but that code does not compile then. If the model object is already instantiated to call GetItemList(), you cannot declare it again. Is Model and MyViewModel two different things? – Borges Apr 06 '18 at 21:15
  • Yes, Model is a property of WebViewPage class and MyViewModel is your model class; instance of which you pass to a view, so you can access it using Model property. – Igor Yalovoy Apr 06 '18 at 21:22
  • Your code will just throw an exception - the model is a collection and does not contain a property named `Value`! –  Apr 06 '18 at 22:10
1

Nothing wrong with the answers offered so far but I'm going to give you a different approach. Bellow you will find code that defines an extension method to any IEnumerable of T. These extension methods convert any list to a SelectList:

namespace System
{
    public static class IEnumerableExtensions
    {
        public static SelectList ToSelectList<T>(this IEnumerable<T> items) where T : class
        {
            return new SelectList(items);
        }

        public static SelectList ToSelectList<T>(this IEnumerable<T> items, object selectedValue) where T : class
        {
            return new SelectList(items, selectedValue);
        }

        public static SelectList ToSelectList<T>(this IEnumerable<T> items, string dataValueField, string dataTextField, object selectedValue) where T : class
        {
            return new SelectList(items, dataValueField, dataTextField, selectedValue);
        }

        public static SelectList ToSelectList<T>(this IEnumerable<T> items, string dataValueField, string dataTextField) where T : class
        {
            return new SelectList(items, dataValueField, dataTextField);
        }
    }
}

Because they are defined in System namespace, these methods will be available to you anywhere in your code. You can now do the following:

Model.GetItemList().ToSelectList();

or

Model.GetItemList().ToSelectList("ValueFieldName", "TextFieldName");

So now you can just pass your list as part of your view model:

public class MyViewModel
{
    // Replace T here with your domain model fetched from Model.GetItemList()
    public List<T> Items { get; set; }
}

public ActionResult Name()
{
    // Previous action logic here...
    var myViewModel = new MyViewModel { Items = Model.GetItemList() }
    return View(myViewModel);
}

and then in your view you can now do this:

// Declare your view model at the top of your view
@model yournamespace.MyViewModel

// Define your drop down list.
@Html.DropDownListFor(n => n.Value, Model.Items.ToSelectList());
Marko
  • 12,543
  • 10
  • 48
  • 58
  • That looks great. How is that ViewModel declared in the view? It is not recogized by that name. I declare the name of my model. But then Items is not recogized. – Borges Apr 06 '18 at 22:05
  • @Borges I was missing the keyword public on the Items property. I updated the MyViewModel class above. – Marko Apr 06 '18 at 22:20
  • Is that MyViewModel declaration in the view separate from the standard model declaration? Or is it instead of it? Because my view does not recognize the MyViewModel as part of the model. – Borges Apr 06 '18 at 22:25
  • The ViewModel goes in the model right? Not the Controller? – Borges Apr 06 '18 at 22:25
  • @Borges I called it MyViewModel because I don't know what you named your view model. The point is to put the public List Items { get; set; } into whatever view model you are declaring on your view. – Marko Apr 06 '18 at 22:27
  • Ah OK! In that case, the ToSelectLIst() extention method isn't reognized as part of Items. If I leave it off, then the DropDownListFor() cannot infer the type. – Borges Apr 06 '18 at 22:33
  • But I need that value to to identify the chosen item right? Do I need to cast it or can I return it back to the controller along with the chosen item? – Borges Apr 06 '18 at 23:26