Razor and Html.DropDownList: Setting selected values for a dropdown for each row in a list?
This seems like it should be simple, but I can't figure out how to make it work.
My data model has a "Server" table, and a "ServerType" table. PKs for both tables are ints, and Server has a field ServerTypeId which is a fk to ServerType.Id.
I have a Razor List.cshtml that is typed to IEnumerable:
@model IEnumerable<Server>
<table border="1">
<tr>
<th>
Server Type
</th>
<th>
Name
</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DropDownListFor(modelItem => item.ServerTypeId, (IEnumerable<SelectListItem>)ViewData["ServerType"])
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
My controller has:
public ActionResult List()
{
var s = GetServers();
ViewData["ServerType"] = GetServerTypes();
return View("List", s);
}
private List<S开发者_如何转开发electListItem> GetServerTypes()
{
string id;
SelectListItem si;
List<SelectListItem> sl = new List<SelectListItem>();
IQueryable<ServerType> items = (from t in _entities.ServerTypes select t);
foreach (var item in items)
{
id = item.Id.ToString();
si = new SelectListItem { Value = id, Text = item.Description };
sl.Add(si);
}
return sl;
}
This displays the data, but the value in the dropdown is not selected. I've tried both Html.DropDownList and Html.DropDownListFor, with different permutation of names for the ViewData property, with and without the Id at the end.
Do I need to create a viewmodel that has copies of the ServerType in order to set the Selected property? Or is it a problem because my ids are ints, and the SelectItemList Value property is a string?
For anyone else still looking for he answer. I had to do 3 things to get this to work
- User @for instead of @foreach. This is so it has an index to work with when naming things.
- Don't have the ViewBag variable name the same as the property. It tries to help you out and binds things too early if they have the same name.
Pass the current value in the constructor for SelectList. My code ended up as:
@for (int i = 0; i < Model.Count; i++) { <tr> <td> @Html.DropDownListFor(modelItem => Model[i].operatorToken, new SelectList(ViewBag.operatorTokensList, "Value", "Text", Model[i].operatorToken), "Select", htmlAttributes: new { @class = "form-control" }) ...ect... }
Notice the 4th parameter to SelectList() sets the selected value.
My operatorTokensList was valued with:
new[] { new { Value = ">", Text = ">" }, new { Value = ">=", Text = ">=" }, new { Value = "=", Text = "=" }, new { Value = "<=", Text = "<=" }, new { Value = "<", Text = "<" } };
(The user was selecting "greater than", "greater than or equal", etc.)
At no point in your population of the List in GetServerTypes() do you specify that any of the items are selected. This is something you need to do manually, as MVC3 isn't smart enough to infer it for you in the DropDownListFor method. This is further complicated by the fact that you are not using a single model.
A better way to do this might be:
(Keep in mind in the below code, I'm assuming that the Server class has a primary id called "Id")
For the controller code:
public ActionResult List()
{
IEnumerable<Server> s = GetServers();
ViewData["ServerTypes"] = GetServerTypes(s);
return View("List", s);
}
private Dictionary<int, SelectList> GetServerTypes(IEnumerable<Server> s)
{
Dictionary<int, SelectList> sl = new Dictionary<int, SelectList>();
IEnumerable<ServerType> items = (from t in _entities.ServerTypes select t);
foreach (Server srv in s) {
sl.Add(srv.Id, new SelectList(items, "Id", "Description", srv.ServerTypeId));
}
return sl;
}
For the view code:
(Also note below how the I've corrected the arguments used in the lambda functions)
@model IEnumerable<Server>
<table border="1">
<tr>
<th>
Server Type
</th>
<th>
Name
</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DropDownListFor(modelItem => modelItem.ServerTypeId, (IEnumerable<SelectListItem>)(ViewData["ServerTypes"][item.Id]))
</td>
<td>
@Html.DisplayFor(modelItem => modelItem.Name)
</td>
</tr>
}
</table>
@TreyE correctly points out that you never specify that any particular select list item should be selected when displayed in the view.
There are several ways you can do this. First is to use the SelectList object and use its constructor that allows you to pass in the object that should be selected, it's the overload SelectList(IEnumerable, String, String, Object)
MSDN SelectList.
SelectList
is only supported on .NET 3.5+ though FYI.
Second, in GetServerTypes()
you could write:
private List<SelectListItem> GetServerTypes()
{
List<SelectListItem> sl = new List<SelectListItem>();
IQueryable<ServerType> items = (from t in _entities.ServerTypes select t);
foreach (var item in items)
sl.add(new SelectListItem { Value = item.id, Text = item.Description, Selected = item.isSelected } );
return sl;
}
Also remember that only one item should be selected, so make sure that if you do try to use some boolean property it is not possible that more than one item
could have its isSelected
property set to true
.
Alternatively, if you need to use some type of if
statement to decide if Selected = true
(i.e. your item has no isSelected boolean) then you can add that in the foreach
loop.
foreach(var item in items)
{
if //condition
sl.Add(new SelectListItem { Value = item.id, Text = item.Description, Selected = true });
else
sl.Add(new SelectListItem { Value = item.id, Text = item.Description, Selected = false });
}
精彩评论