I have an action that retrieves data and sends it to a view. In a view I have two dropdown menus.
First drop down shows salutation (such as "Mr.", "Ms.", etc.) and does not select value I sent for some reason. The other dropdown shows language list and correctly selects value I sent to the view. The relevant code in view is shown below.
#Html.DropDownListFor(model => model.Salutation, ViewBag.salutation as IEnumerable<SelectListItem>)
#Html.DropDownListFor(model => model.Language, ViewBag.languages as IEnumerable<SelectListItem>)
In the controller I have the following code to get the dropdown data.
ViewBag.salutation = new List<SelectListItem>() {
new SelectListItem() { Text = "", Value = "" },
new SelectListItem() { Text = "Mr.", Value = "Mr." },
new SelectListItem() { Text = "Ms.", Value = "Ms." },
new SelectListItem() { Text = "Mrs.", Value = "Mrs." }
};
and
var languages = (from l in db.Languages.ToList()
select new SelectListItem()
{
Text = l.Language,
Value = l.LanguageId.ToString()
}).ToList();
languages.Insert(0, new SelectListItem() { Text = "", Value = "" });
ViewBag.languages = languages;
The only difference I could think of is that the languages dropdown has an integer as value, whereas salutation dropdown has text as value. Is this why the salutation dropdown doesn't work? I know I could go through each salutation List<SelectListItem> item and set Selected property based on the value I retrieved from database. But I was hoping there would be a cleaner way to do this.
Any ideas?
Thanks
UPDATE
I decided to do what I did for another project.
IList<SelectListItem> _salutation = new List<SelectListItem>()
{
new SelectListItem() { Value = "", Text = "" },
new SelectListItem() { Value = "Mr.", Text = "Mr." },
new SelectListItem() { Value = "Ms.", Text = "Ms." },
new SelectListItem() { Value = "Mrs.", Text = "Mrs." }
};
// I could put the following in the declaration above, but for testing purposes it's in foreach loop.
foreach (var item in _salutation)
{
// compare to what's retrieved from database
item.Selected = item.Value == _viewData.Salutation;
}
ViewBag.salutation = _salutation;
After foreach loop I output .Value, .Selected property of each item in _salutation and I get all the correct values with one item being selected. Inside the view I did the following.
#foreach (var item in ViewBag.salutation as IEnumerable<SelectListItem>)
{
<b>#item.Value : #item.Text : #item.Selected</b><br />
}
All the correct Text/Values come up but none are Selected! This happens if I output the values after I execute #Html.DropDownListFor(). If I output the ViewBag.salutation before the html helper the correct value is selected.
SOLUTION
I found the following article useful: DropDownListFor with ASP.NET MVC.
Instead of using ViewBag I added the following to the ViewModel. (Showing the part for salutations drop down.)
public class TheViewModel
{
private IList<string> _salutations = new List<string>() { "", "Mr.", "Ms.", "Mrs." };
public IEnumerable<SelectListItem> SalutationItems
{
get
{
var salutations = _salutations.Select(s => new SelectListItem { Value = s, Text= s });
return salutations;
}
}
// The rest of the ViewModel
}
And in the View I have the following.
#Html.DropDownListFor(model => model.Salutation, Model.SalutationItems)
Instead of just supplying the list to the DropDownListFor helper you could provide it a SelectList. The SelectList constructor takes the list and allows you to explicitly set the selected value as well as an overload that lets you specify the Text and Value fields.
#Html.DropDownListFor(model => model.Salutation,
new SelectList(ViewBag.salutation as IEnumerable<SelectListItem>,
"Value", "Text", Model.Salutation))
Try this,
#Html.DropDownListFor(m =>m.DDCountryModel,IEnumerable<SelectListItem>)ViewBag.salutation)
#Html.DropDownListFor(model => model.Language, IEnumerable<SelectListItem>)ViewBag.languages)
Your Model should be like this,
public class Model
{
public IEnumerable<SelectListItem> DDCountryModel{ get; set; }
public IEnumerable<SelectListItem> Language{ get; set; }
}
Related
I'm having problems trying to get a preselected value to work.
I've tried to include Selected in the SelectListItem but it doesn't pre-select.
Any clues as to why it's not matching up? Thanks for any help.
RetailerId is int. (it's not defined as enumerable)
Retailer is an enum, for example:
public enum Retailer
{
Sears = 10,
Macys = 20
}
Here is the View code :
#Html.DropDownListFor(x => x.RetailerId,
Enum.GetValues(typeof(Retailer)).Cast<Retailer>()
.OrderBy(o => o.GetDescription())
.Select(o => new SelectListItem() { Text = o.GetDescription(), Value = o.ToString(), Selected = (o.ToInt() == Model.RetailerId) }),
new { #data_placeholder = "Select Retailer", #class = "form-control" })
Your generating a collection of SelectListItem where the Value is either "Sears" or "Macys" which cannot be bound to property RetailerId which is an int.
Note that the Selected property of SelectListItem is ignored when binding to a model property. Internally the method sets the Selected property based on the value of the property your binding to, and since its an int, it does not match any of your options values and the first option is selected because something has to be.
You can make this work by modifying the .Select clause to
.Select(o => new SelectListItem() { Text = o.GetDescription(), Value = ((int)o).ToString() }),
Alternatively, change the model property to
public Retailer RetailerId { get; set; }
an the .Select clause to
.Select(o => new SelectListItem() { Text = o.GetDescription(), Value = o.ToString() }),
View
#Html.DropDownListFor(m => m.InsertCustomer.CountryID, Model.lstCountry, new { #class = "DropDownListFor" })
#Html.ValidationMessageFor(m =>m.InsertCustomer.CountryID)
View Model
[Required(ErrorMessage = "Please Select Country")]
public string CountryID { get; set; }
Method to create a list for the dropdown
public IEnumerable<SelectListItem> getCountry()
{
DNC_DAL.clsCustomerMaster _objDalUser = new DNC_DAL.clsCustomerMaster();
DataTable dtCountry = new DataTable();
dtCountry = _objDalUser.GetCountry();
List<SelectListItem> lstCountry = new List<SelectListItem>();
SelectListItem firstOption = new SelectListItem() { Text = "---Select One---" };
lstCountry.Add(firstOption);
foreach (DataRow drCountry in dtCountry.Rows)
{
SelectListItem Country = new SelectListItem() { Text = drCountry["DCM_DESC"].ToString(), Value = drCountry["DCM_ID"].ToString() };
lstCountry.Add(Country);
}
return lstCountry;
}
Controller
public ActionResult wfrmCustomerMaster()
{
Models.clsCustomerMaster CustomerModel = new Models.clsCustomerMaster();
IEnumerable<SelectListItem> strCountry = null;
strCountry = CustomerModel.getCountry();
CustomerModel.lstCountry = strCountry;
return View(CustomerModel);
}
All the other validations( Not posted in the question) work perfectly on the page except for the dropdown validation, I wonder why?
Your code is adding the first option as
<option>---Select One---</option>
which does not have a value="" attribute, which means if you select it, the value of the <select> element will be "---Select One---", which is valid (i.e. its not null or an empty string).
Instead, to generate a label option with a null value, use the overload that accepts a optionLabel, which will generate the first option as
<option value="">---Select One---</option>
and remove the code in the getCountry() which generates this option
I'm having trouble assigning the selected value to a select list (that is in a table) from a DB.
The value is defaulting to the first option even when a value is passed to it and it definitely passing from my controller to the view.
I have also tried a different select list that passes the value correctly on my form, and that select list also doesn't work. So I'm pretty sure it has something to do with the table
Here is a screenshot of the value passing.
Select List
public IEnumerable<SelectListItem> ListViewStatusOptions
{
get
{
return new[]
{
new SelectListItem { Value = "Scheduled", Text = "Scheduled" },
new SelectListItem { Value = "OFR-Out for Revision", Text = "OFR-Out for Revision" },
new SelectListItem { Value = "Tool Work Required", Text = "Tool Work Required" },
new SelectListItem { Value = "Delayed", Text = "Delayed" },
new SelectListItem { Value = "In Press", Text = "In Press" },
};
}
}
Select List in Table
#foreach (var item in Model) {
<td>
#Html.DropDownListFor(modelItem => item.ListViewStatus, item.ListViewStatusOptions, "Select", new { #id = #Html.ValueFor(modelItem => item.Id )})
</td>
}
You need to use a SelectList object new an array of SelectListItems.
Add this property to your method
public IEnumerable<SelectListItem> StatusOptions
{
get { return new SelectList(ListViewStatusOptions, "Value", "Name"); }
}
"Value" is the name of the property that contains the id of each item. You can have a list of any type of object not just SelectListItems. This is why is may seem unnecessary to pass "Value", Similarly, "Name" is the property that contains what to show the user.
Then change your razor code to
#foreach (var item in Model) {
<td>
#Html.DropDownListFor(modelItem => item.ListViewStatus, item.StatusOptions, "Select", new { #id = #Html.ValueFor(modelItem => item.Id )})
</td>
}
the proper binding for a list of values will be:
for (int i = 0; i < Model.Count; i++)
{
#Html.DropDownListFor(modelItem => modelItem[i].ListViewStatus, new SelectList(Model[i].ListViewStatusOptions, "Value", "Text", Model[i].ListViewStatus))
}
you have to use for loop instead of for-each because mvc will not generate proper input names if foreach is used and the posted data will be not resolved by action methods
My updated Razor Code.
As #ScottMacMaster explains,
You need to use a SelectList object new an array of SelectListItems.
"Value" is the name of the property that contains the id of each item.
You can have a list of any type of object not just SelectListItems.
This is why is may seem unnecessary to pass "Value", Similarly, "Name"
is the property that contains what to show the user.
I was receiving an error for Name so i changed it to Text
I then assign item.ListViewStatus as the object selected value
#Html.DropDownListFor(modelItem => item.ListViewStatus,
new SelectList(item.ListViewStatusOptions, "Value", "Text",item.ListViewStatus), "Select",
new { #id = #Html.ValueFor(modelItem => item.Id )})
I am using the following to generate a drop down list:
#for (var index = 0; index < Model.AdminSummaries.Count(); index++)
{
<div class="rep_tr0">
<div class="rep_td0">
#Html.DropDownListFor(x => Model.AdminSummaries[index].Status,
AdminStatusReference.GetAdminStatusOptions(),
new { id = string.Format("Status_{0}",index ) })
</div>
</div>
}
Here's the HTML it generates:
<select id="Status_1" name="AdminSummaries[1].Status"><option value="1">Released</option>
<option value="2">Review</option>
<option value="3">New</option>
</select>
Here's the class that gives the status options.
public static class AdminStatusReference
{
public static IEnumerable<SelectListItem> GetAdminStatusOptions()
{
return new[]
{
new SelectListItem { Value = "1", Text = "Released" },
new SelectListItem { Value = "2", Text = "Review" },
new SelectListItem { Value = "3", Text = "New" }
};
}
}
Everything works good EXCEPT it doesn't select the items correctly. There's no option with 'selected' to match the data in the AdminSummaries.
How can I make it so the correct select list items are selected?
Just to clarify this. My problem is that if there is a data record with a value of 3 for the status then when I look at the screen I see a select list with the word "Release" showing.
What I need is for the select list to show text that corresponds with the data value.
Here is the more accurate answer
public static class AdminStatusReference
{
public static IEnumerable<SelectListItem> GetAdminStatusOptionsFor(AdminSummaries arg)
{
var options = new[]
{
new SelectListItem { Value = "1", Text = "Released" },
new SelectListItem { Value = "2", Text = "Review" },
new SelectListItem { Value = "3", Text = "New" }
};
options.First(o=> o.Value == arg).Selected = true;
return options;
}
}
Set the SelectListItem.Selected property to true:
public static class AdminStatusReference
{
public static IEnumerable<SelectListItem> GetAdminStatusOptions()
{
return new[]
{
new SelectListItem { Value = "1", Text = "Released", Selected = true },
new SelectListItem { Value = "2", Text = "Review" },
new SelectListItem { Value = "3", Text = "New" }
};
}
}
It seams from the source code that the DropDownListFor method (actually the ViewDataEvaluator.Eval method) doesn't support expressions containing indexers. Because your expression: AdminSummaries[index].Status contains an indexer that's why the framework doesn't use the selected value from your model class.
The only solution is to specify the selected item when setting the SelectListItem collection, you can do this by passing the currently selected value to your GetAdminStatusOptions method:
View:
#Html.DropDownListFor(x => Model.AdminSummaries[index].Status,
AdminStatusReference.GetAdminStatusOptions(Model.AdminSummaries[index].Status),
new { id = string.Format("Status_{0}",index ) })
A sample GetAdminStatusOptions implementation:
public static IEnumerable<SelectListItem> GetAdminStatusOptions(string selected = null)
{
var options = new[]
{
new SelectListItem {Value = "1", Text = "Released"},
new SelectListItem {Value = "2", Text = "Review"},
new SelectListItem {Value = "3", Text = "New"}
};
foreach (var option in options)
{
option.Selected = option.Value == selected;
}
return options;
}
I am using a dropdown list as follows.
<%=Html.DropDownList("ddl", ViewData["Available"] as SelectList,
new { CssClass = "input-config", onchange = "this.form.submit();" })%>
On its selection change I am invoking post action. After the post the same page is shown on which this drop down is present. I want to know about the HTML attribute for the drop down which will let me preserve the list selection change. But as of now the list shows its first element after the post.
e.g. The dropdoen contains elements like 1,2,3,etc. By default 1 is selected. If I select 2, the post is invoked and the same page is shown again but my selection 2 goes and 1 is selected again.
How can preserve the selection?
Thanks,
Kapil
You have to make the list of select list items again and tell which of the items is the selected one in every post (Selected property of the SelectListItem).
When you perform the post you will be setting the ViewData["Available"] again, you can set the select item here. So when you create the drop down list in the html the selected item is already selected. So your code could look something like:
ViewData["Available"] = new SelectList( items, "dataValueField", "dataTextField", "selectedValue" );
You have to take the property model ddl, or receive it as a parameter in the action, such as:
public ActionResult Action(Model model, string ddl)
Then to create ViewData [" Available "], you have to pass it as selected value
public ActionResult Action(Model model, string ddl)
{
ViewData["Available"] = List<SelectListItem>
{
new SelectListItem { Text = "1", Value = "1", Selected = (ddl == "1") },
new SelectListItem { Text = "2", Value = "2", Selected = (ddl == "2") },
new SelectListItem { Text = "3", Value = "3", Selected = (ddl == "3") }
};
return View(model);
}
OR:
public ActionResult Action(Model model, string ddl)
{
var list = List<SelectListItem>
{
new SelectListItem { Text = "1", Value = "1" },
new SelectListItem { Text = "2", Value = "2" },
new SelectListItem { Text = "3", Value = "3" }
};
ViewData["ddl"] = new SelectList(list, "value", "text", ddl);
return View(model);
}
EDIT: See also this
This worked for me:
<%=Html.DropDownList("Ibus", ViewData["Ibus"] as SelectList, new { **#class** = "dASDropDown" })%>