I have a many to many relationship between cars and many other tables. In my view I want to be able to have a multiselect list for items such as lights. I've tried many different formats I can't seem to get the list to set selected items.My preferred way to do it would be @Html.ListBoxFor(model => model.enitity.relatedentity, Model.SomeSelectListItemList) , but I can not seem to get list to set selected items in the view.
Below is my code I was testing with I use Asp.net MVC5, C# and entity framework 6.
This is my controller action
// GET: Car/Edit/
[Authorize(Roles = "Ecar")]
public ActionResult Edit(int id)
{
CarsEditViewModel carEditViewModel = new CarsEditViewModel();
carEditViewModel.Cars = unitOfWorkcar.CarRepository.FindIncluding(id);
IList<Approval> approvalList = unitOfWorkcar.ApprovalRepository.All.ToList();
IList<Connector> connectorList = unitOfWorkcar.ConnectorRepository.All.ToList();
IList<InputVoltage> inputVoltagesList = unitOfWorkcar.InputVoltageRepository.All.ToList();
carEditViewModel.ApprovalList = from c in approvalList select new SelectListItem { Text = c.Name, Value = c.Id.ToString(), Selected = true};
carEditViewModel.Categories = new MultiSelectList(carEditViewModel.ApprovalList, "Value", "Text", "Selected");
// ,carEditViewModel.ApprovalList.Select(c => c.Text),carEditViewModel.ApprovalList.Select(c => c.Selected)
//carEditViewModel.ApprovalList = from c in approvalList select new MultiSelectList( //{ Selected = (carEditViewModel.Cars.Approvals.Any(app => app.Id == c.Id)) , Text = c.Name, Value = c.Id.ToString() };
// carEditViewModel.ConnectorList = from c in connectorList select new SelectListItem { Selected = true, Text = c.Name, Value = c.Id.ToString() };
carEditViewModel.InputVoltageList = from c in inputVoltagesList select new SelectListItem { Text = c.Name, Value = c.Id.ToString() };
return View(carEditViewModel);
}
Here is my view
@model NewBobPortal.ViewModels.CarsEditViewModel
@using (Html.BeginForm())
{
@* @Html.ListBoxFor("SelectedApprovals",model => model., new { @class = "multiselect" })*@
@*@Html.ListBoxFor(model => model.Cars.Approvals, Model.ApprovalList)
@Html.ListBoxFor(model => model.Cars.Connectors,Model.ConnectorList, new {Multiple = "multiple"})
@Html.ListBoxFor(model => model.ConnectorList, Model.ConnectorList)*@
@*@Html.ListBox("test",Model.Cars.InputVoltages, Model.InputVoltageList)*@
@Html.DropDownList("somethingelse", new MultiSelectList(Model.InputVoltageList, "Value", "Text", Model.InputVoltageList.Select(c => c.Value)), new { multiple = "multiple" })
@Html.DropDownListFor(model => model.Cars.InputVoltages , new MultiSelectList(Model.LensColorList, "Value", "Text", Model.LensColorList.Select(c => c.Value)), new { multiple = "multiple" })
@Html.ListBoxFor(m => m.Cars.Approvals, Model.Categories)
<p>
<input type="submit" value="Save" />
</p>
}
This is my viewmodel
using NewBobPortal.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace NewBobPortal.ViewModels
{
public class CarsEditViewModel
{
public Car Cars { get; set; }
public IEnumerable<SelectListItem> ApprovalList { get; set; }
public IEnumerable<MultiSelectList> ConnectorList { get; set; }
public IEnumerable<SelectListItem> InputVoltageList { get; set; }
public MultiSelectList Categories { get; set; }
public IEnumerable<Approval> SelectedCategories { get; set; }
}
}
The biggest problem with posting values for a many-to-many relationship is that there's no direct field to bind to on your model. This is where view models become very handy, which you're already using, but not quite in the right way for this.
First you need your SelectList
, which can actually just be an IEnumerable<SelectListItem>
. This will contain all available options, which is easy enough. So in your view model:
public IEnumerable<SelectListItem> CategoryChoices { get; set; }
And in your action:
carEditViewModel.CategoryChoices = approvalList.Select(m => new SelectListItem {
Text = c.Name,
Value = c.Id.ToString()
});
Notice that I'm not setting Selected
: we'll let the HtmlHelper handle that. I'm also not dealing with a MultiSelectList
yet either.
Now, you'll also need something to post back to, since your values will be ids, we'll use a List<int>
, so in your view model:
private List<int> selectedCategories;
public List<int> SelectedCategories
{
get
{
if (selectCategories == null)
{
selectedCategories = Categories.Select(m => m.Id).ToList();
}
return selectedCategories;
}
set { selectedCategories = value; }
}
There's a bit going on here. The set
method of the property is simple: when we get a posted value back, just set selectedCategories
to that. The get
is a bit more complicated: here we need to condense down your list of category objects (called Categories
here because I don't know where this is actually coming from) into a simple list of ids for those categories.
Now, in your view:
@Html.ListBoxFor(m => m.SelectedCategories, Model.CategoryChoices)
That's all you need. You're using a ListBox
control so it's already a multiple select list. And, by binding it to the list of all currently selected ids, it knows which items to select automatically in the list of SelectListItem
s it gets from Model.CategoryChoices
.
In your post action, you then need to translate these ids into their associated objects:
var newCategories = repository.Categories.Where(m => carEditViewModel.SelectedCategories.Contains(m.Id));
Then, you can set your model's categories to this new list manually:
car.Categories = newCategories;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With