1

I used scaffolding to get a simple MVC page and it produced code like this from my model:

ViewData["CustomersNo"] = new SelectList(_context.Customers, "CustomersId", "Name");

Then I scaffolded the view which produced markup like this:

<div class="form-group">
    <label asp-for="CustomersNo" class="col-md-2 control-label"></label>
    <div class="col-md-10">
        <select asp-for="CustomersNo" class ="form-control" asp-items="ViewBag.CustomersNo"></select>
    </div>
</div>

And when rendered it produced a dropdown control with my customers from my database. Exactly what I wanted and I didn't even have to write a line of code.

But now I want to create a dropdown on a more customized view using filtered data. No matter what I do, it renders as a multiselect which I definitely do not want.

I want to use a View Model but I've also tried ViewData since that is what the scaffolding used:

proViewModel.Vols = new SelectList(_context.Products.Where(t => t.Type == "Volume"), "ModelId", "ModelId");
ViewData["VolModels"] = new SelectList(_context.Products.Where(t => t.Type == "Volume"), "ModelId", "ModelId");

My View Model is pretty simple so far:

public class ProductsViewModel
{
    [Key]
    public int ID { get; set; }
    [NotMapped]
    //public IEnumerable<string> Vols { get; set; }
    //public IEnumerable<SelectListItem> Vols { get; set; }
    public SelectList Vols { get; set; }
    [NotMapped]
    public string SelectedModel { get; set; }
}

I tried different IEnumerable types since that was what most tutorials used. But the scaffolding uses SelectList and this SO Q&A as well as this tutorial both say that IEnumerable will create a multiselect and seem to suggest using SelectList.

But with this markup, all I can get is multiselect:

<td>
    <div class="form-group">
        <label asp-for="Vols" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <select asp-for="Vols" class="form-control" asp-items="Model.Vols"></select>
        </div>
    </div>
</td>
<td>
    <div class="form-group">
        <label asp-for="Vols" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <select asp-for="Vols" class="form-control" asp-items="ViewBag.VolModels"></select>
        </div>
    </div>
</td>

I can't see a difference between what the scaffolding is doing and what I am doing. But the rendered output is completely different and not what I want. So what am I missing here?

embedded.kyle
  • 10,976
  • 5
  • 37
  • 56

1 Answers1

1

You cannot bind a <select> to a collection of complex objects, which is what Vols is. A <select> binds to, and posts back a simple type and a <select multiple> binds to, and posts back an array of a simple type.

In addition, the name of the property your binding to and the name of the SelectList cannot be the same (refer Can the ViewBag name be the same as the Model property name in a DropDownList? for more detail).

Because the property your binding to is IEnumerable, a <select multiple> is created.

Change your code to

<div class="form-group">
    <label asp-for="SelectedModel" class="col-md-2 control-label"></label>
    <div class="col-md-10">
        <select asp-for="SelectedModel" class="form-control" asp-items="Model.Vols"></select>
    </div>
</div>

and the selected option value will be bound to the SelectedModel property.

As a side note, remove your [Key] and [NotMapped] attributes - they a EF specific attributes and have no place in a view model.

  • Added those attributes a few days ago to get past some compile errors. I didn't think they belonged either. No problems now. Don't know what I did elsewhere to fix that. – embedded.kyle Oct 04 '17 at 21:34
  • As another side note, I would also recommend making your property `public IEnumerable Vols { get; set; }` - `SelecList` is `IEnumerable` but not the other way around (that then gives you the flexibility of using `_context.Products.Where(...).Select(x => new SelectListItem{ ... });` or `new SelectList(_context.Products.Where(...).Select(x => x.ModelId));` –  Oct 04 '17 at 22:01
  • Got it. Was wondering why every example was `IEnumerable`. Makes sense if it's more flexible. – embedded.kyle Oct 04 '17 at 23:46
  • I must have just compiled and not run last night. If I delete the `[Key]` or `[NotMapped]` attributes, when I run but after I log in I get, "The entity type 'SelectListGroup' requires a primary key to be defined" and points to the line in `public async Task Index()` where I pull the contents of a table into a variable before passing it to a view. Am I missing some step in defining it as a view model as opposed to an EF model? – embedded.kyle Oct 05 '17 at 18:49
  • It sounds like your associating your view model with EF. View models should not be in your `/Models` folder. They need to be in a separate folder (say) `/ViewModels` –  Oct 05 '17 at 21:41
  • Okay. I'll try that tomorrow. The scaffolding put its own view models in sub-folders under `/Models`. So I followed suit. Guess I need to follow the tutorials more closely instead of trying to imitate the scaffolding. – embedded.kyle Oct 06 '17 at 00:09