I have a table that contains a list of EquipmentIDs and another table that has maintenance records.
When the user edits a maintenance record I want there to be a drop down list of all of the equipment IDs from the table.
The dropdown list populates, and it populates with the correct amount of entries, however they all say System.Web.MVC.SelectListItem instead of the value of the ID.
Here is the code that generates the list:
public ActionResult Edit(int id)
{
MaintPerformed maintPerformed = maintPerformedRepository.GetMaintPerformed(id);
IList<EquipmentID> IDs = equipmentIDRepository.GetEquipmentIDAsList();
IEnumerable<SelectListItem> selectEquipList =
from c in IDs
select new SelectListItem
{
//Selected = (c.EquipID == maintPerformed.EquipID),
Text = c.EquipID,
Value = c.Sort.ToString()
};
ViewData["EquipIDs"] = new SelectList(selectEquipList, maintPerformed.ID);
return View(maintPerformed);
}
Here is the entry in the .aspx page for the Dropdown list:
%: Html.DropDownList("EquipIDs") %>
Here is how I am generating the list from the table:
public List<EquipmentID> GetEquipmentIDAsList()
{
return db.EquipmentIDs.ToList();
}
It appears that everything is working correctly with the exception of assigning the text to be displayed in the drop down box.
What am I missing or not thinking correctly about?
SelectList and SelectListItem are actually mutually exclusive. You should be using one or the other. Etiher pass the constructor of SelectList your raw data (IDs) or don't use SelectList at all and just make ViewData["EquipIDs"] your enumerable of SelectListItem. If you go with the latter approach, you will have to tweak your code so that you are setting the selected item in the constructor of SelectListItem (as you had done, but commented out).
Either:
ViewData["EquipIDs"] = new SelectList(IDs, maintPerformed.ID, "EquipID", "Sort");
Or:
IEnumerable<SelectListItem> selectEquipList =
from c in IDs
select new SelectListItem
{
Selected = c.EquipID == maintPerformed.EquipID,
Text = c.EquipID,
Value = c.Sort.ToString()
};
ViewData["EquipIDs"] = selectEquipList;
Related
My model contains an array of zip code items (IEnumerable<SelectListItem>).
It also contains an array of selected zip codes (string[]).
In my HTML page, I want to render each selected zip code as a drop down with all the zip code options. My first attempt did not work:
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes", Model.ZipCodeOptions )
}
I realized that although that would produce drop downs with the right "name" attribute, it wouldn't know which element of ZipCodes holds the value for that particular box, and might just default to the first one.
My second attempt is what really surprised me. I explicitly set the proper SelectListItem's Selected property to true, and it still rendered a control with nothing selected:
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes", Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}
There, it's returning a new IEnumerable<SelectListitem> that contains all the original items, unless it's the selected item, in which case that element is a new SelectListItem with it's Selected property set to true. That property is not honored at all in the final output.
My last attempt was to try to use an explicit index on the string element I wanted to use as the value:
#{int zipCodeIndex = 0;}
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes[" + (zipCodeIndex++) + "]", Model.ZipCodeOptions )
}
That doesn't work either, and probably because the name is no longer "ZipCodes", but "ZipCodes[x]". I also received some kind of read-only-collection error at first and had to change the type of the ZipCodes property from string[] to List<string>.
In a forth attempt, I tried the following:
#for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
var zip = Model.ZipCodes[zipCodeIndex];
Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions )
}
That produces controls with id like "ZipCodes_1_" and names like "ZipCodes[1]", but does not select the right values. If I explicitly set the Selected property of the right item, then this works:
#for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
var zip = Model.ZipCodes[zipCodeIndex];
Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}
However, the problem with that approach is that if I add a new drop downs in JavaScript and give them all the name "ZipCodes", then those completely override all the explicitly indexed ones, which never make it to the server. It doesn't seem to like mixing the plain "ZipCodes" name with explicit array elements "ZipCodes[1]", even though they map to the same variable when either is used exclusively.
In the U.I., user's can click a button to add a new drop down and pick another zip code. They're all named ZipCodes, so they all get posted to the ZipCodes array. When rendering the fields in the loop above, I expect it to read the value of the property at the given index, but that doesn't work. I've even tried remapping the SelectListItems so that the proper option's "Selected" property is true, but it still renders the control with nothing selected. What is going wrong?
The reason you first 2 snippets do not work is that ZipCodes is a property in your model, and its the value of your property which determines what is selected (not setting the selected value in the SelectList constructor which is ignored). Since the value of ZipCodes is an array of values, not a single value that matches one of the option values, a match is not found and therefore the first option is selected (because something has to be). Note that internally, the helper method generates a new IEnumerable<SelectListItem> based on the one you provided, and sets the selected attribute based on the model value.
The reason you 3rd and 4th snippets do not work, is due to a known limitation of using the DropDownListFor() method, and to make it work, you need to use an EditorTemplate and pass the SelectList to the template using AdditionalViewData, or construct a new SelectList in each iteration of the loop (as per your last attempt). Note that all it needs to be is
for(int i = 0; i < Model.ZipCodes.Length; i++)
{
#Html.DropDownListFor(m => m.ZipCodes[i],
new SelectList(Model.ZipCodeOptions, "Value", "Text", Model.ZipCodes[i]))
}
If you want to use just a common name (without indexers) for each <select> element using the DropDownList() method, then it needs to be a name which does not match a model property, for example
foreach(var item in Model.ZipCodes)
{
#Html.DropDownList("SelectedZipCodes",
new SelectList(Model.ZipCodeOptions, "Value", "Text", item))
}
and then add an additional parameter string[] SelectedZipCodes in you POST method to bind the values.
Alternatively, use the for loop and DropDownListFor() method as above, but include a hidden input for the indexer which allows non-zero based, non consecutive collection items to be submitted to the controller and modify you script to add new items using the technique shown in this answer
Note an example of using the EditorTemplate with AdditionalViewData is shown in this answer
i have List view and in every rows it has the "Actualizar" link action and that one takes me to the edit view in which i have some dropdownLists available for updating my existing data but my big problem is that i want the dropdownlist to first load the data is save. how can i set the dropdownlist to load the data of the model a return and let me choose other options
controller:
public ActionResult ActualizarDispositivo(int id )
{
dropDown();
var equipo= _db.equipo.Include(m=>m.marca).Include(mo=>mo.modelo.Single(b => b.idEquipo==id);
ViewBag.idMarca = _db.marca.ToList().Select(Mar => new SelectListItem
{
Value = Mar.idMarca.ToString(),
Text = Mar.Descripcion.ToString(),
Selected = true
});
return View("ActualizarDispositivo",equipo);
}
in the edit view i have this
#Html.DropDownListFor(m=>m.idMarca,(IEnumerable<SelectListItem>)ViewBag.idMarca)
The code you are using should work, if you simply remove the Selected = true. Currently you are setting all of the DropDownList's values to selected.
I'm not sure which performs better, but I do find it more readable to create a select list using:
ViewBag.idMarca = new SelectList(_db.marca.ToList(), "idMarca", "Descripcion");
And then, there may be no need to cast. (Unsure, I avoid the use of ViewBag)
#Html.DropDownListFor(m=>m.idMarca, ViewBag.idMarca)
I have a class, Farm, which contains a list of classes of type Animal.
public class Farm {
public list<Animal> Animals;
// other members..
}
public class Animal {
public string Name;
public string Family;
}
I would like to make a createOrEdit view for my Farm object, and I'd like to use DropDownLists for the Animal's Name and Family. The choices are coming from a database.
When I pass the Animal model to the view for editing I'd like to have the DropDownLists somehow match the properties for each animal and set the selected values of the lists.
I've tried lots of things like this:
#for(int i = 0; i < Model.Animals.Count; i++)
{
#Html.DropDownListFor(model => model.Animals[i].Name, Model.AnimalNames)
// where Model.AnimalNames is a SelectList
#Model.Animals[i].Name // (for testing) this properly displays the name I want to be selected in the list
}
I've seen a bunch of suggestions on this site for creating SelectLists in the controller, iterating through each item and setting the selected property where appropriate. But there's gotta be a cleaner way.. what if I have 100 Animals on my farm. It doesn't seem reasonable to create 200 SelectLists in the controller, iterate through each of them to match up the selected values, and then pass that to the view.
So, is there a simple way for me to take that Animal.Name[i] value and find its matching listitem in the DDL?
Thanks!!
Inside your foreach loop you can do:
#for(int i = 0; i < Model.Animals.Count; i++)
{
var original = Model.AnimalNames;
var animalSelected = new SelectList(original.Items, original.DataValueField, original.DataTextField, model.Animals[i].Name);
#Html.DropDownListFor(model => model.Animals[i].Name, animalSelected)
}
This way, in your controller you create just one SelectList with the AnimalNames... and in your view, you create SelectList with the selected value for each one.
We use this approach when using list of editable items with the need of display the current value of each item.
MVC 3 VB.NET razor. I have a view that has 4 dropdown boxes in it.. This is for setting up a staff member.. If that staff member is to work certain classes then that class will be set for each day. If he is not then the value needs to stay null. This is a edit view so it may have to be accessed multiple times and still keep the original selectlist values if none changed. The below is what I have right now that is working only on its face. The Old selected value is shown first. However this isnt being returned on the save... The only way that it will save correctly is if I select the value that was set in each box then click save. The next problem is that not all staff members will have classes to work on every one of the 4 days. So how do I set a value to null and keep it that way unless a class Is actually selected..
Dim _staff As confstaff = db.confstaffs.Single(Function(a) a.id = id)
ViewBag.role = _staff.Conf_Role.ToString
ViewBag.confRole = db.conf_roles.ToList
ViewData("tue_Class") = New SelectList(db.courses.ToList.Where(Function(r) r.course_day = "Tuesday").Select(Function(r) r.course_ref), New With {.value = _staff.tue_class})
ViewData("wed_Class") = New SelectList(db.courses.ToList.Where(Function(r) r.course_day = "Wednesday").Select(Function(r) r.course_ref), New With {.value = _staff.wed_class})
ViewData("thur_Class") = New SelectList(db.courses.ToList.Where(Function(r) r.course_day = "Thursday").Select(Function(r) r.course_ref), New With {.value = _staff.thur_class})
ViewData("fri_Class") = New SelectList(db.courses.ToList.Where(Function(r) r.course_day = "Friday").Select(Function(r) r.course_ref), New With {.value = _staff.fri_class})
Return View(_staff)
And the view is:
<label>Tuesday Class</label>
#Html.DropDownList("tue_class", "Select One")
<label class="small_spacing">Wednesday Class</label>
#Html.DropDownList("wed_class", "Select One")
<label class="small_spacing">Thursday Class</label>
#Html.DropDownList("thur_class", "Select One")
<label class="small_spacing">Friday Class</label>
#Html.DropDownList("fri_class", "Select One")
I already expect someone to point out that I should use a view model instead of viewbag but I dont see how a view model would be practical with there being over 100 different courses but I am open for ideas...
Any Ideas???????
if you look at your page source after the page is loaded you will see your top item set from your viewbag value has no value only text so when it is submitted it thinks you are submitting blank. I ran into this before.
What you need to do is to manually create each of your dropdown lists by iterating through the collection and setting the one that matches your viewbag item to selected, that way you can be sure a selected item has a selected value. I have some Razr code around here somewhere. Will update when I find it.
EDIT
<select name="Type1" id="Type1">
<option value=""></option>
#foreach (var name in ViewBag.BackOfficeTypes)
{
if (name == ViewBag.SelectedType1Value)
{
<option value="#name" selected="selected">#name</option>
}
else
{
<option value="#name">#name</option>
}
}
</select>
I have several of these on the same page building from different sets of items. Hope this helps.
EDIT 2
If you want to do it all from codebehind I am not a VB guy but here is a way to do it but you need to change your linq statement to manually create the list items instead of dumping from the toList Method.
var courses = db.getCourses();
IEnumerable<SelectListItem> selectList =
from c in courses
where c.course_day = "Tuesday"
select new SelectListItem
{
Selected = (c.CourseID == selectedCourseID),
Text = c.Name,
Value = c.CourseID.ToString()
};
If you can translate this into the VB equivalent it might solve your issue instead of building them in the Razor end.
I'm trying to use a SelectList one of my views, and its just not populating correctly. It gets the proper number of entries (4), but they all read System.Web.Mvc.SelectListItem. I fired up the debugger on the code, and saw some strangeness going on. I must be doing something wrong, but I don't quite see what.
Code from the ViewModel:
public SelectList DeviceTypes {get; private set;}
....
var device_types = DataTableHelpers.DeviceTypes();
IEnumerable<SelectListItem> sl = device_types.Select(
dt => new SelectListItem { Selected = (dt.DeviceType == 1),
Text = dt.Description,
Value = dt.DeviceType.ToString() }).ToList();
DeviceTypes = new SelectList(sl);
And code from the View:
<%= Html.DropDownList("Type",Model.DeviceTypes) %>
So, when I look at this in the debugger, the sl IEnumerable is getting built correctly. I can see all 4 elements in there, with the proper Text and Value property values. Once I call the SelectList constructor however, if I expand the IEnumerable that it contains, I see that it has 4 entries, but all the data in them has been lost. The Text is set to System.Web.Mvc.SelectListItem, and the value is null.
Ive tried changing the ToList() call to a ToArray(), as well as removing it entirely. That didn't change the behaviour.
What am I doing wrong here?
EDIT: Scratch my first answer.
You should be passing the IEnumerable list if items to the View, not trying to construct a Html item in the controller.
Code for controller:
public IEnumberable<YourModel> DeviceTypes {get; internal set;}
....
DeviceTypes = DataTableHelpers.DeviceTypes();
Code for View:
<%= Html.DropDownList("Type", from dt in Model.DeviceTypes
select new SelectListItem
{
Text = dt.Description,
Value = dt.DeviceType.ToString(),
Selected = dt.DeviceType == 1
}) %>