I'm building an MVC web application and am about to run my head through the wall, there's a dozen topics on this already and all have accepted solutions that do not work for me. There's probably something really stupid and simple I'm forgetting/not seeing, but can't seem to work it out.
Please don't hate me for naming; I'm continuing a project started by someone else.
I have the following line in my ManageDetail.cshtml:
#Html.DropDownListFor(m => m.ChecklistWaarde.SoortID, Model.ChecklistOptionsOptions, "Choose a value", new { #class = "form-control" })
This builds a selectbox based on property Model.ChecklistWaarde.SoortID, which is an integer. The next parameter passed is a SelectList from the model that is generated by the following very simple helper function that does a DB lookup in a 2ndary database (unfortunately), it receives the parameter SoortID as well to set a preselect value:
public SelectList GetChecklistOptions(int selectedId = -1)
{
var soorten = new List<SelectListItem>();
try
{
using (var connection = new SqlConnection(mssqlConnectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandText = "Select Naam, ID From ChecklistSoorten";
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
var id = reader["ID"] as int?;
if (id.HasValue)
{
soorten.Add(new SelectListItem { Text = reader["Naam"] as string, Value = id.Value.ToString(), Selected = id.Value.Equals(selectedId) });
}
}
}
}
catch (Exception ex)
{
Globals.Log.Error(ex);
}
return new SelectList(soorten, "Value", "Text");
}
It gets called in the controller like this:
public ActionResult ManageDetail(int? id) {
var model = new MachineDetailsModel(id);
model.ChecklistOptionsOptions = sqlDal.GetChecklistOptions(model.ChecklistWaarde.SoortID);
return View("ManageDetail", model);
}
When I try to debug my code, I can see that the model values are populated correctly:
SoortID has a value of 2 (int)
Model.ChecklistOptionsOptions has a populatedSelectList
Upon inspecting the SelectList I find that only item with value "2" has property Selected = true, this is false for all others.
When the form gets rendered, it will not select the item with value 2, rather it will have the first item selected, "Choose a value"
Inspecting the HTML, I see that the item with value 2 is indeed not selected, contrary to what I was expecting (see HTML below; shorted the list in count and text for readability). I expected to see "selected" on the line for item 2, as it was selected in the SelectList.
<select class="form-control" data-val="true" data-val-number="The field SoortID must be a number." data-val-required="The SoortID field is required." id="ChecklistWaarde_SoortID" name="ChecklistWaarde.SoortID" style="min-width:100%;">
<option value="">Choose a value</option>
<option value="1">Item A</option>
<option value="2">Item B</option>
<option value="3">Item C</option>
<option value="4">Item D</option>
</select>
What am I missing? How can I make item with value "2" select, as it is in the SelectList that I pass to the DropDownListFor() function?
In the controller where you are populating SelectList, set selected value there, your code would look like:
if (id.HasValue)
{
soorten.Add(new SelectListItem
{
Text = reader["Naam"] as string,
Value = id.Value.ToString()
});
}
and then down at the line of instantiating SelectList:
return new SelectList(soorten, "Value", "Text",selectedId);
or you could elminate SelectList and instead in your model change the property ChecklistOptionsOptions type to List<SelectListItem>, in that case the last line of creating SelectList will not be required further, right now you are creating it multiple times.
Ultimately everything in the view that gets rendered comes from ViewData. This takes the model, but also request data and 'ModelState`.
I have had this sort of problem when some of the request parameters have a name conflict with some of the model parameters. This overrides the value in the model and causes the issue.
It is very difficult to debug, as it normally appears that all the correct values have been set. Check all your parameter names if in doubt.
Related
Been googling for a while, but I can't seem to get any closer.
I'm using MVC with an EF Database structure.
I want the dropdownlist items in the View to show up with different names than what they come up with.
Right now, the lambda query returns a list().
I want each item to get a string name depending on their current name. Eventually, the selected field needs to be used in another lambda as the Byte that it was.
Edit
//view
<p>
#using (Html.BeginForm("Index", "Home", FormMethod.Get))
{
<p>
#Html.DropDownList("vbType", (SelectList)ViewBag.Type, "" , new { onchange = "form.submit();" })
....
//controller
var typeLst = new List<byte>();
var typeQry = from t in db.model1
orderby t.TYPE
select t.TYPE;
typeLst.AddRange(typeQry.Distinct());
ViewBag.vbType = new SelectList(typeLst);
....
Thanks in advance.
You can create a SelectListItem to display in your dropdownlist.
Example:
#Html.DropDownListFor(model => model.YearStartedToPay,
Enumerable.Range(DateTime.Today.Year - 60, 61).OrderByDescending(x => int.Parse(x.ToString())).Select(n => new SelectListItem() { Text = n.ToString(), Value = n.ToString(), Selected = false }).ToList<SelectListItem>(),....)
In this case, I'm creating an Enumerable, that will be your list returned from EF, ordering and selecting as a SelectListItem.
You need to make use of dropdownlist's Value property. Make your list a type of SelectListItem.
Assign Text= [Your Required Text] and Value = [Current Name]
I have a list created from a controller which is passed to a viewbag item:
List<SelectListItem> PTL = new List<SelectListItem>();
List<PT> PTL2 = db.PT.ToList();
foreach (var item in PTL2)
{
PTL.Add(new SelectListItem { Value = item.ID.ToString(), Text = item.Name });
}
ViewBag.PTL2 = PTL2;
Then, in the view, I tried the following from another question here:
#Html.DropDownList("test", new SelectListItem[]{
new SelectListItem() {Text = "Exemplo1", Value="Exemplo1"},
new SelectListItem() {Text = "Exemplo2", Value="Exemplo2"},
new SelectListItem() {Text = "Exemplo3", Value="Exemplo3"}}
, htmlAttributes: new { #class = "form-control" })
which worked fine, but, if I try to edit it to the following:
#Html.DropDownList("test", ViewBag.PTL2, htmlAttributes: new { #class = "form-control" })
I get various errors.
I have spent hours on this trying different combinations, different castings and more, but, I just don't seem to be making progress.
I have seen so many different errors - the most common one being '
There is no ViewData item of type IEnumerable<SelectListItem> that
has the key “test”'
However, I tried casting and changing the name as per different questions here, but, I just can't seem to get past this.
At this point, I am thinking of just making a manual HTML Drop Down list and a foreach loop for the content, but, this seems a waste.
Does anyone know what I have done wrong?
The second parameter must be a collection of System.Web.Mvc.SelectListItem objects that are used to populate the drop-down list. But, you are using ViewBag property in this case and the type of this is dynamic. So, you must cast it to the collection of the SelectListItem. Otherwise, you will get this error in MVC 4+:
Extension methods cannot be dynamically dispatched
So. as a result just change
ViewBag.PTL2
to
ViewBag.PTL2 as IEnumerable<SelectListItem>.
i hope it is not too late ,this is how i do it with .net core
in your controller ,use below code
public ActionResult ShowCalendar()
{
var programs = new Microsoft.AspNetCore.Mvc.Rendering.SelectList((new Programs().GetAll().ToList(), "ID", "Name")
ViewBag.AllItems = programs;
return View();
}
and in your cshtml file use the below
<select id="ItemID" class="form-control"
asp-items="ViewBag.AllItems">
<option disabled selected>--- #Localizer["PleaseSelect"] ---</option>
</select>
will do the job for you
I have many DropDownLists on page
class BigViewModel
{
public List<SmallViewModel> SmallVM {get;set;}
public List<SelectListItem> Items {get;set;}
//some other properties
}
class SmallViewModel
{
public string ItemId {get;set;}
//some other properties
}
<table>
#for( var i = 0;i<Model.SmallVM.Count();i++)
{
<tr>
<td>
#Html.DropdownListFor(m=> m.SmallVM.ItemId, Model.Items)
</td>
</tr>
}
//display other properties
</table>
in controller
bigViewModel.Items = List<SelectListItem>
{
new SelectListItem{Value = "1", Text = "aaa"},
new SelectListItem{Value = "2", Text = "bbb"},
new SelectListItem{Value = "3", Text = "ccc"},
}
bigViewModel.SmallVM = new List<SmallViewModel>
{
new SmallViewModel{ItemId = 3},
new SmallViewModel{ItemId = 2},
}
In controller I set diffrent ItemId for every SmallVM and each DropDownList uses the same Items collection. I want to set default Value from SmallViewModel for each DropDownList. For example in this case there are two DropDownLists first should display default text "ccc" and second "bbb".
Should I put diffrent List<SelectedListItem> for every SmallViewModel and set them Selected property or there is other way?
This behavior has been reported as a bug on CodePlex but not yet fixed. Using DropDownListFor() in a for loop does not bind correctly and the first option is always selected despite the value of the property. In order for DropDownListFor() to work correctly when using a collection, you need to use an EditorTemplate for the model.
In /Views/Shared/EditorTemplates/SmallViewModel.cshtml
#model SmallViewModel
#Html.DropdownListFor(m => m.ItemId, (SelectList)ViewData["Items"])
Then in the main view
#model BigViewModel
#using(Html.BeginForm())
{
// Pass the select list to the EditorTemplate as addtionalViewData
#Html.EditorFor(m => m.SmallVM, new { Items = Model.Items })
<input type="submit" />
}
You should now have 2 <select> controls displaying "ccc" and "bbb" respectively.
Based on your code updates, I think you just need to modify your view code to:
#Html.DropdownListFor(m=> m.SmallVM[i].ItemId, Model.Items)
However, I have distinct feeling that this is very much an XY problem, and although this change will make everything wire up on post, you're still not going to be getting what you actually need for the true problem you're trying to solve.
I am creating a list of months for a list box. The controller captures the selected months and stores them in session for if the user navigates away from the page then returns.
Here is the controller:
public ActionResult Index(int[] Months)
{
if (Session["Months"] == null || Months!= null)
Session["Months"] = Months;
else if (Months== null)
Months= Session["Months"] as int[];
IList<SelectListItem> MonthsList = utility.GetMonths().OrderBy(r => r.Name)
.Select(r => new SelectListItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = Months == null ? false : Months.Contains(r.Id)
}).ToList();
var model = new DataModel
{
SelectList = MonthsList,
Data = GetDataByMonths(Months)
};
return (model);
}
Here is the view:
#Html.ListBox("Months", Model.SelectList)
When the user selects items from the ListBox they are highlighted even after the form has been submitted. However when the user navigates away then returns the SelectListItems are correctly labeled as Selected = true but the DOM does not show this.
Any ideas on why this only doesnt work when session is used?
EDIT:
Tried:
#Html.ListBox("Months", new MultiSelectList(Model.Months, "Value", "Text", Model.SelectedMonths), new { size = 8 })
While debugging, the variables show the correct values, they are just not correctly highlighted in the DOM.
Did you try the SelectList type or MultiSelectList rather than an IEnumerable<SelectListItem>? I don't know of any other way to tell the ListBox which property is the value and which is the text.
i.e.
MultiSelectList list = new MultiSelectList(monthsList, "Value", "Text");
and return the list object?
I ended up creating the list box in html rather than using the #html.listbox()
<select name="Months" id="Months" multiple="multiple" size="8">
#foreach (var a in Model.Months)
{
<option value="#a.Value" #(a.Selected.ToString() == "True" ? "selected='selected'" : "")>#a.Text</option>
}
</select>
I'm looping over my model but the value that is coming from the model isn't what's showing in the drop down list as the selected item. Instead the list is always just showing the first entry.
#Html.DropDownListFor(modelItem => item.RecordType, Model.RecordTypes, new { id="recordType_" + item.TransactionID })
item.RecordType has a value of 5 from the model (I print it out so I know it's that value). However the drop down shows "Initial" (which is a value of 4) instead of "Firm" (which is the valu eof 5 which is what our model item value is). The list in the source is defined as:
<select data-val="true" data-val-number="The field RecordType must be a number." id="recordType_63" name="item.RecordType">
<option value="4">Initial</option>
<option value="5">Firm</option>
<option value="6">Announced</option>
<option value="7">N/A</option>
</select>
How do I set the drop down to be what the model value is?
public IEnumerable<SelectListItem> RecordTypes
{
//get { return new SelectListItem(recordTypes, "RECORD_TYPE_ID", "RECORD_TYPE"); }
get
{
return (from r in recordTypes
select new SelectListItem { Text = r.RECORD_TYPE, Value = r.RECORD_TYPE_ID.ToString() });
}
}
You can do that by creating a SelectList in your Html.DropDownListFor like:
#Html.DropDownListFor(modelItem => item.RecordType, new SelectList(Model.RecordTypes, item.RecordType), new { id="recordType_" + item.TransactionID });
This passes your IEnumerable<SelectListItem> into the constructor along with a parameter, in this case item.RecordType, letting it know which item to select.
EDIT: In response to comment
You could use the overload of SelectList which allows you to specify the DataTextField and DataValueField in your case these would be Text and Value respectively.
or
Re-work your Model.RecordTypes to return a SelectList and do the necessary work in there, you could use a method and pass in the value to select. Something like:
public SelectList RecordTypes(object selectedValue)
{
return new SelectList(recordTypes, "RECORD_TYPE", "RECORD_TYPE_ID", selectedValue);
}
Hope that helps.
When you are assembling your RecordTypes SelectListItem collection, make sure to set Selected = true for the currently selected item-- it will not set it for you.
Assuming the RecordType variable on your view model mates up against the RECORD_TYPE_ID variable of your data set, you can update creation of variable to:
from r in recordTypes
select new SelectListItem { Text = r.RECORD_TYPE, Value = r.RECORD_TYPE_ID.ToString(), Selected = r.RECORD_TYPE_ID == this.RecordType }
If you are re-using the RecordTypes variable across multiple dropdowns on the view, you're going to have to change your approach and create the IEnumerable<SelectListItem> for each dropdown. Suchas:
#Html.DropDownListFor(modelItem => item.RecordType, RecordTypes.Select(rt => new SelectListItem { Text = rt.Text, Value = rt.Value, Selected = rt.Value == item.RecordType}))