.NET Core <select> With Default Value? - c#

My goal is to generate a <select> dropdown with a certain select value (<option>) pre-selected, so the finalized HTML should look like this:
<select>
<option value = "1">Option1</option>
<option value = "2" selected>Option2</option>
<option value = "3">Option3</option>
</select>
Note that the option with a value of 2 has a selected attribute, indicating that it will show before the dropdown is clicked. I cannot (and am trying to) achieve this using .NET Core Tag Helpers.
This is my model (I think you can safely ignore the details):
public class ReportViewModel
{
[Display(Name = "Pick a form")]
public IEnumerable<Form> AvailableForms { get; set; }
public Form SelectedForm { get; set; }
public List<ReportData> FormResponses { get; set; }
}
Note that the Form object there just has Id and Name properties on it.
This is my select statement:
<label asp-for="SelectedForm.Name" class="form-control-label font-weight-bold"></label>
<select asp-for="SelectedForm.Name"
class="form-control"
onchange ="onFormSelected(this.value)"
asp-items="#(new SelectList(Model.AvailableForms, "Id", "Name"))">
</select>
I don't see the answer in the Microsoft Guide
Or in a blog on the topic

Basically you need to is set the select to use the SelectedForm.Id property, then specify the value of the form to be selected in your controller. I had to update your code a little but this works for me -
<label asp-for="SelectedForm" class="form-control-label font-weight-bold"></label>
<select asp-for="SelectedForm.Id"
class="form-control"
onchange ="onFormSelected(this.value)"
asp-items="#(new SelectList(Model.AvailableForms, "Id", "Name"))">
</select>
Then in your controller
var vm = new ReportViewModel()
{
AvailableForms = new List<Form>()
};
var form2 = new Form() { Id = 2, Name = "Bar" };
(vm.AvailableForms as List<Form>).Add(new Form() { Id = 1, Name = "Foo" });
(vm.AvailableForms as List<Form>).Add(form2);
(vm.AvailableForms as List<Form>).Add(new Form() { Id = 3, Name = "Baz" });
vm.SelectedForm = form2;
return View(vm);

The SelectList constructor can take an additional argument, namely selectedValue. In your case, you should be able to pass this through, which I think will look something like this:
asp-items="#(new SelectList(Model.AvailableForms, "Id", "Name", Model.SelectedForm.Id))"
I think the reason asp-for isn't working for you is the mismatch between the value of SelectedForm.Name (which is a string), and the value of the Id property you're using in the SelectList constructor (which is an integer?).
You can see partially how this works in the source code. You end up here in GenerateOption:
var selected = item.Selected;
if (currentValues != null)
{
var value = item.Value ?? item.Text;
selected = currentValues.Contains(value);
}
At this point, I believe currentValues will contain a single element, which will be a string (Form.Name). I believe also that item.Value will be e.g. "1" and so there's no match.

This is old, but I didn't see my approach here.
Pass it in with a viewbag from the controller.
<select>
<option>#ViewBag.selected</option>
<option>1</option>
<option>2</option>
<option>3</option>
</select>

Related

Asp.net Core, set multiple selected values in Razor Page

So, I am trying to create a multi-select dropdown list and I want to set some default selected values in it. I get the values and store them in an array of strings containing the values in each one. I tried to set them as 1 string and it didn't work, tried setting them as an array of strings and it didn't work as well.
Here is the code:
<select name="FacultyList" multiple class="sel form-control" asp-for="Faculties"
id="uniFaculties" asp-items="#(new SelectList(facultyList,"Name","Name",#selectedFaculties))">
</select>
facultyList is a list of values I get from and api.
selectedFaculties is an array of strings that contains the selected values.
Note: I'm using the Chosen jQuery library as well if that makes a difference.
There is a MultiSelectList that you can use for this:
new MultiSelectList(facultyList, "Name", "Name", #selectedFaculties)
I just noticed from your code that you're using asp-for on the select. When doing that, the selected values you pass to either SelectList or MultiSelectList will be ignored. The reason is because it's expecting the selected values to be in the Faculties property that you're binding to. In case you run into that, here is a working example for you.
View
<select name="FacultyList" multiple class="sel form-control" asp-for="SelectedFaculties"
id="uniFaculties" asp-items="#(new MultiSelectList(Model.AvailableFaculties, "Name", "Name"))">
</select>
Controller
public IActionResult Faculty()
{
var vm = new FacultyViewModel
{
AvailableFaculties = new List<Faculty>
{
new Faculty
{
Name = "Arts"
},
new Faculty
{
Name = "Science"
},
new Faculty
{
Name = "Mathematics"
}
},
SelectedFaculties = new List<string> { "Arts", "Mathematics" }
};
return View(vm);
}
This will select Arts and Mathematics. Notice that the selected values are coming from the same property that we're binding the select to using asp-for, and that we're no longer passing selected values to the MultiSelectList directly.

MVC SelectList setting random value as 'selected'

I have a hard coded select list, pulling Value and Text from the db.
I have a specific item I want to set as the "default", so I order the list to specifically put it first in line ("Wip"). However, when the list is rendered in HTML, the order of the list is correct, but the selected value is the item with the smallest value, even though it is at the bottom of the list due to ordering descending.
Model:
public IEnumerable<SelectListItem> BinOptionsList { get; set; }
Data Access:
public IEnumerable<SelectListItem> PopulateBinList()
{
using (var db = new InventoryContext())
{
var data = db.StorageBin.OrderByDescending(s => s.BinCode == "Wip").ThenBy(s => s.BinCode).Select(s => new SelectListItem()
{
Text = s.BinCode,
Value = s.BinId.ToString()
}).ToList();
return data;
}
}
Controller:
model.BinOptionsList = dal.PopulateBinList()
View:
#Html.DropDownListFor(model => model.BinId, Model.BinOptionsList, new { #class = "form-control" })
The list renders in the View with "Wip" as the first selection, but the rendered "selected=selected" option is the item with id=0.
I know I can set the 'Selected' value in the Data Access method, but then I cant use this in an edit form that would already have a selected value.
Update - Rendered HTML:
<select class="form-control" data-val="true" data-val-number="The field Bin Code must be a number." data-val-required="The Bin Code field is required." id="BinId" name="BinId">
<option value="582">WIP</option>
<option value="595">0888</option>
<option selected="selected" value="0">0919</option>
<option value="1">1</option>
<option value="2">1A1</option>
<option value="3">1A10</option>
<option value="4">1A11</option>
</select>
I'm pretty sure BindId is equal to 0. If BindId is a simple Integer then it will always be equal to 0 by default. You'll need to set it to 582 yourself or set it as Integer? which would set the default as null.
So you may not prefer this answer but let me outline one quick thing. Your select list should not be of your model. The value selected from your select this should be part of your model. You are putting too much overhead into your model and it is having in impact on your view. Refactor it. Put your select list in your ViewBag for you to use in your view, stop passing it back and forth. This will keep your model compact and allow you to set your options properly.
Here is a simple example.
What would be your Data Access (just stubbing it out for example sake and yes setting it to static for example simplicity):
public static IEnumerable<SelectListItem> PopulateBinList()
{
return new List<SelectListItem>{ new SelectListItem {
Text = "WIP", Value = "582"
}, new SelectListItem {
Text = "0888", Value = "595"
} , new SelectListItem {
Text = "0", Value = "0919"
}, new SelectListItem {
Text = "1", Value = "1"
}};
}
Then your model (just adding the one property for example)
public class BinModel
{
public string BinId { get; set; }
}
In your controller
public ActionResult Index()
{
//load up your select list into the view bag
ViewBag.BinOptions = ViewHelper.PopulateBinList();
//preset the value of your desired object
var bm = new BinModel { BinId = "582" };
return View(bm);
}
The in your view you are going to bind to model property but use your temp list defined in the view bag to fill the options. Presetting your model's property will auto select which value is defaulted in the select list:
#Html.DropDownListFor(x => x.BinId, (IEnumerable<SelectListItem>)ViewBag.BinOptions, null, new { #class ="form-control" })
Please let me know if this does not make sense or needs to be expanded on.

Add item to #Html.DropDownList with option value and set default selected

I am rendering a select list in my view like so:
#Html.DropDownList("SelectCategory", (SelectList)ViewBag.Categories, "All")
I populate it like so:
ViewBag.Categories = new SelectList(db.Categories, "Id", "Name");
That renders:
<select id="SelectCategory" name="SelectCategory">
<option value="">All</option>
<option value="1">Fruit</option>
<option value="44">T-Shirts</option>
</select>
Issues:
1) The option value for All is empty, how can I put my value there, say 0 ?
2) How can I set a default selected value in #Html.DropDownList ?
The 3rd parameter of the DropDownList() method adds a 'label' option with a null value. Typically the option text is something like "Please select" and its purpose is to force a user to make a valid selection. If the label option is selected, a null value is submitted and ModelState is invalid (assuming the property your binding to is required).
If you want an additional option with <option value="0">All</option>, then you need to generate that in the SelectList you pas to the view, for example
List<SelectListItem> categories = db.Categories.Select(x => new SelectListItem()
{
Value = x.Id.ToString(), // assumes Id is not already typeof string
Text = x.Name
}).ToList();
categories.Insert(0, new SelectListItem(){ Value = "0", Text = "All" }) // Or .Add() to add as the last option
ViewBag.Categories = categories;
and in the view (note remove the 3rd parameter is you do not want the label option)
#Html.DropDownList("SelectCategory", (IEnumerable<SelectListItem>)ViewBag.Categories, "Please select")
In order to 'select' an option initially, you need to set the value of the property your binding to before you pass the model to the view, so if the value of property SelectCategory is "0", the the "All" option will be selected when the view is first displayed. If its "44", then the "T-Shirts" option will be selected. If the value of SelectCategory does not match one of the option values, or is null, then the first option will be selected (because soemthing has to be)
You could build your select "by hands"
<select>
#foreach (var item in optionList)
{
if(myCondition)
{
<option value="#item.Value" selected="selected">#item.Text</option>
}
else
{
<option value="#item.Value">#item.Text</option>
}
}
</select>
or using Linq in the view
var list = optionsList.Select(x => new SelectListItem { Text = x.Text, Value = x.Value, Selected = myCondition });
then you could used that list in one of the Html.DropdownList
Here is the full example
int catId = // Gets the catId to select somehow
IEnumerable<SelectListItem> options = optionsList
.Select(x => new SelectListItem {
Text = x.Text,
Value = x.Value,
Selected = x.Value == catId
}); // catId
And then you use it like this :
#Html.DropdownList("id-of-the-dropdown", options);

Passing in more than one value from a DropDown list

I need to pass in 2 values from the DropDown to the Action method :
#using (Html.BeginForm("AddMemberToEvent"))
{
#Html.DropDownList("ddlPeopleList",
new SelectList(Model.PeopleList, "Id", "Username"), "Select: ",
new { onchange = "this.form.submit();", eventID = Model.catID })
}
[HttpPost]
public ActionResult AddMemberToEvent(string ddlPeopleList, string eventID)
{
The ddlPeopleList parameter works, . . and I'm a little confused on what that first parameter is for the DropDown list, . . ., is it the name of the DropDown or is it the Selected value used by the code?
The eventID is not being passed in. I put a break point at the dropdown and the correct value was there, it's just not being passed to the method.
Is there a better way to implement this? thanks
UPDATE : I solved it by doing this. Using a hidden field :
#using (Html.BeginForm("AddMemberToEvent"))
{
#Html.Hidden("eventID", Model.catID )
Different constructor of Html.DropDownList is all down to the model binding in the controller action side.
In the action, we have code to populate a List
public ActionResult Index()
{
List<SelectListItem> items = new List<SelectListItem>();
items.Add(new SelectListItem { Text = "Action", Value = "0" });
items.Add(new SelectListItem { Text = "Drama", Value = "1" });
items.Add(new SelectListItem { Text = "Comedy", Value = "2", Selected = true });
items.Add(new SelectListItem { Text = "Science Fiction", Value = "3" });
ViewBag.MovieType = items;
return View();
}
In the client side, first consider the easiest constructor which got 1 parameter which is the name of the dropdown.
#Html.DropDownList("MovieType")
When it is rendered, the html is
<select id="MovieType" name="MovieType"><option value="0">Action</option>
<option value="1">Drama</option>
<option selected="selected" value="2">Comedy</option>
<option value="3">Science Fiction</option>
</select>
see here the id and name tag of the select is MovieType, when it is posted to the action, the model binder will try to bind the value of this select to a property named MovieType in the target binder object.
Now consider a little bit complex one
#Html.DropDownList("dropdown", (List<SelectListItem>)ViewBag.MovieType)
the html for it is:
<select id="dropdown" name="dropdown"><option value="0">Action</option>
<option value="1">Drama</option>
<option selected="selected" value="2">Comedy</option>
<option value="3">Science Fiction</option>
</select>
note that the only difference is the id and name tag, it changes the behaviour of model binder which this time it is gonna find the property of dropdown in the target binding object.
now lets see the most complicated scenario which the constructor have 4 parameters.
#Html.DropDownList("dropdown1", (List<SelectListItem>)ViewBag.MovieType, "Movie Type", new { xxx = "abc" })
Html part:
<select id="dropdown1" name="dropdown1" xxx="abc">
<option value="">Movie Type</option>
<option value="0">Action</option>
<option value="1">Drama</option>
<option selected="selected" value="2">Comedy</option>
<option value="3">Science Fiction</option>
</select>
id and name is dropdown1, html attribute is xxx and value of xxx is abc. Movie Type is inserted into the first place of into the selectlistitem collection and the value is empty.
Model binder will try to bind dropdown1 to target binding object.
To answer your second question, the model binder will not retrieve value from html attribute that's why eventId is not populated.

ASP.NET MVC DropDownListFor with model of type List<string>

I have a view with a model of type List<string> and I want to place a drop down list on the page that contains all strings from the list as items in the drop down. I am new to MVC, how would I accomplish this?
I tried this:
#model List<string>
#Html.DropDownListFor(x => x)
but that threw an error.
To make a dropdown list you need two properties:
a property to which you will bind to (usually a scalar property of type integer or string)
a list of items containing two properties (one for the values and one for the text)
In your case you only have a list of string which cannot be exploited to create a usable drop down list.
While for number 2. you could have the value and the text be the same you need a property to bind to. You could use a weakly typed version of the helper:
#model List<string>
#Html.DropDownList(
"Foo",
new SelectList(
Model.Select(x => new { Value = x, Text = x }),
"Value",
"Text"
)
)
where Foo will be the name of the ddl and used by the default model binder. So the generated markup might look something like this:
<select name="Foo" id="Foo">
<option value="item 1">item 1</option>
<option value="item 2">item 2</option>
<option value="item 3">item 3</option>
...
</select>
This being said a far better view model for a drop down list is the following:
public class MyListModel
{
public string SelectedItemId { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
and then:
#model MyListModel
#Html.DropDownListFor(
x => x.SelectedItemId,
new SelectList(Model.Items, "Value", "Text")
)
and if you wanted to preselect some option in this list all you need to do is to set the SelectedItemId property of this view model to the corresponding Value of some element in the Items collection.
If you have a List of type string that you want in a drop down list I do the following:
EDIT: Clarified, making it a fuller example.
public class ShipDirectory
{
public string ShipDirectoryName { get; set; }
public List<string> ShipNames { get; set; }
}
ShipDirectory myShipDirectory = new ShipDirectory()
{
ShipDirectoryName = "Incomming Vessels",
ShipNames = new List<string>(){"A", "A B"},
}
myShipDirectory.ShipNames.Add("Aunt Bessy");
#Html.DropDownListFor(x => x.ShipNames, new SelectList(Model.ShipNames), "Select a Ship...", new { #style = "width:500px" })
Which gives a drop down list like so:
<select id="ShipNames" name="ShipNames" style="width:500px">
<option value="">Select a Ship...</option>
<option>A</option>
<option>A B</option>
<option>Aunt Bessy</option>
</select>
To get the value on a controllers post; if you are using a model (e.g. MyViewModel) that has the List of strings as a property, because you have specified x => x.ShipNames you simply have the method signature as (because it will be serialised/deserialsed within the model):
public ActionResult MyActionName(MyViewModel model)
Access the ShipNames value like so: model.ShipNames
If you just want to access the drop down list on post then the signature becomes:
public ActionResult MyActionName(string ShipNames)
EDIT: In accordance with comments have clarified how to access the ShipNames property in the model collection parameter.
I realize this question was asked a long time ago, but I came here looking for answers and wasn't satisfied with anything I could find. I finally found the answer here:
https://www.tutorialsteacher.com/mvc/htmlhelper-dropdownlist-dropdownlistfor
To get the results from the form, use the FormCollection and then pull each individual value out by it's model name thus:
yourRecord.FieldName = Request.Form["FieldNameInModel"];
As far as I could tell it makes absolutely no difference what argument name you give to the FormCollection - use Request.Form["NameFromModel"] to retrieve it.
No, I did not dig down to see how th4e magic works under the covers. I just know it works...
I hope this helps somebody avoid the hours I spent trying different approaches before I got it working.

Categories