Basics of ASP.NET MVC Dropdowns
Learn one of the fundamental elements of the web developer's craft, how to build a dropdown. In this tutorial, we'll be using ASP.NET and C# to craft our dropdown.
Join the DZone community and get the full member experience.
Join For FreeDropdowns are a key of user interface (UI) controls.
In ASP.NET WebForms, it was simple to attach a data source to a DropdownList control and display the dropdown on the form.
When ASP.NET MVC entered the picture, there were some...shall we call them differences, between MVC and WebForms.
In our first post of our dropdown series, we'll go through a simple example of a dropdown on the client-side and a dropdown from a database.
Our Simple Dropdown
In ASP.NET MVC, server controls are mostly tucked away in HtmlHelpers (and in Core, they are TagHelpers).
For example, if you want to create a simple TextBox, you use the HtmlHelper extension method in your View.
@Html.TextBox("MyTextBox", "MyValue", new { @class = "text-right" })
TextBoxes are easy. Let's look at the Dropdown syntax.
@Html.DropDownList("myDropDown", /*<dropdown list of items>*/, /* HtmlAttributes */)
This is one of the overloaded methods when using a Dropdown HtmlHelper. There are a few more, but for now, we'll focus on this one.
While you can add dropdown list items directly in your View, it doesn't mean you should.
Look at the following Dropdown HtmlHelper.
@Html.DropDownList("Months", new List<SelectListItem>
{
new SelectListItem {Text = "January - 01", Value="1" },
new SelectListItem {Text = "February - 02", Value="2" },
new SelectListItem {Text = "March - 03", Value="3" },
new SelectListItem {Text = "April - 04", Value="4" },
new SelectListItem {Text = "May - 05", Value="5" },
new SelectListItem {Text = "June - 06", Value="6" },
new SelectListItem {Text = "July - 07", Value="7" },
new SelectListItem {Text = "August - 08", Value="8" },
new SelectListItem {Text = "September - 09", Value="9" },
new SelectListItem {Text = "October - 10", Value="10" },
new SelectListItem {Text = "November - 11", Value="11" },
new SelectListItem {Text = "December - 12", Value="12" }
}, new {@class="form-control"})
While this gives us our desired result, it looks a little messy, doesn't it?
Seeing this in a View not only muddies the HTML waters, but it makes it hard to read. This creates what I call the Reese's Cup Dilemma. You are mixing code with Views. The separation of concerns is not there at all.
What happens if you need to use this Month dropdown in multiple Views? Don't tell me you'd cut-and-paste. I've already been down this path before as noted in my 5 Major MVC mistakes.
We need to pass the list into the View from the Controller through the ViewModel.
What Does a Dropdownlist Look Like?
A DropDownList is made up of a list of SelectListItems. I've always used a List of SelectListItems to build my dropdowns and it always seems to work.
There are also extension methods I've used in the past to handle this type of "busy work" when converting a list to a DropdownList. I've mentioned this in my DropdownList Cookbook.
Once you have your data, you can plug the data into your ViewModel.
Dropdown in a ViewModel
ViewModels are simply objects holding data to pass into the View. The View is strictly a template and it "plugs" data into certain areas you define in your View.
Here is our sample ViewModel with our state's data.
Models/MonthViewModel.cs
public class MonthViewModel
{
private readonly string[] _months = CultureInfo.CurrentCulture.DateTimeFormat.MonthNames;
}
We have our Months, but how do we create a DropDownList from it?
We let our ViewModel perform some of the heavy lifting by building the dropdown list for ourselves, as mentioned above (previous post: Make your ViewModels more functional).
Models/MonthViewModel.cs
public class MonthViewModel
{
private readonly string[] _months = CultureInfo.CurrentCulture.DateTimeFormat.MonthNames;
public IEnumerable<SelectListItem> GetMonthSelectList(int defaultValue)
{
return _months
.Where(month => !string.IsNullOrEmpty(month))
.Select((e, i) => new SelectListItem
{
Text = e + " - " + (i + 1).ToString(),
Value = (i + 1).ToString(),
Selected = (i + 1 == defaultValue)
});
}
}
The ViewModel delivers our data, but also helps in sending over a pre-built Dropdown list to our View.
If we look at our list in the View (Html), you'll see we have a cleaner dropdownl list syntax.
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { @class = "form-horizontal" }))
{
<div class="form-group">
@Html.Label("Month", "Month:", new { @class = "col-sm-2 control-label" })
<div class="col-sm-3">
@Html.DropDownList("Months",
Model.GetMonthSelectList(DateTime.UtcNow.Month),
new { @class = "form-control" })
</div>
</div>
}
As a result of passing your data in, your HTML becomes cleaner.
Let's Get Strong...Typed
After a user selects the month, we want to save it, but when we post the data back, our ViewModel won't reconstruct the DropdownList. It only passes back the value.
But where is it stored?
We need to add another property to our ViewModel to hold our value.
public class MonthViewModel
{
private readonly string[] _months = CultureInfo.CurrentCulture.DateTimeFormat.MonthNames;
public int SelectedMonth { get; set; }
public IEnumerable<SelectListItem> GetMonthSelectList(int defaultValue)
{
return _months
.Where(month => !string.IsNullOrEmpty(month))
.Select((e, i) => new SelectListItem
{
Text = e + " - " + (i + 1).ToString(),
Value = (i + 1).ToString(),
Selected = (i + 1 == defaultValue)
});
}
}
So we added a SelectedMonth property to our ViewModel and this provides a strong-typed View using the ViewModel.
Our previous HTML had a "magic string" called Months that would hold our value.
Since we have a place to store our value in the SelectedMonth property, we modify our DropDownList syntax to become more strong-typed.
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { @class = "form-horizontal" }))
{
<div class="form-group">
@Html.Label("Month", "Month:", new { @class = "col-sm-2 control-label" })
<div class="col-sm-3">
@Html.DropDownListFor(model => model.SelectedMonth,
Model.GetMonthSelectList(DateTime.UtcNow.Month),
new { @class = "form-control" })
</div>
</div>
}
This DropDownListFor gives MVC's ModelBinder a clue as to where to store the value on a postback: in the model's SelectedMonth property.
I prefer this method because if I "fat-finger" a property name, I won't know at run-time. I'll know at design-time.
Getting Data From a Database
If you're retrieving data from a database, this effort simply adds another step: make a database call using ADO or Entity Framework.
Once you assign the months list to your ViewModel, you are done.
The ViewModel, when requested, will build your dropdown list for you when it reaches the View using our GetMonthSelectList.
Models/MonthViewModel.cs
public class MonthViewModel
{
public string[] MonthData { get; set; }
public int SelectedMonth { get; set; }
public IEnumerable<SelectListItem> GetMonthSelectList(int defaultValue)
{
return MonthData
.Where(month => !string.IsNullOrEmpty(month))
.Select((e, i) => new SelectListItem
{
Text = e + " - " + (i + 1).ToString(),
Value = (i + 1).ToString(),
Selected = (i + 1 == defaultValue)
});
}
}
Controllers/HomeController.cs
public class HomeController : Controller
{
private MonthRepository _repository = new MonthRepository(
ConfigurationManager.ConnectionStrings["DropdownDatabase"].ConnectionString);
public ActionResult Index()
{
// We only want a list of months in a single array.
var model = new MonthViewModel
{
MonthData = _repository
.GetAll()
.Select(item => item.MonthName).ToArray()
};
return View(model);
}
}
The repository is simply an ADO call to pull data from the table.
Since everything is done on the C# side, our HTML in our View doesn't need to be modified at all.
Conclusion
As you can see, using dropdowns aren't difficult when integrating them into your ASP.NET MVC application.
Once you determine what kind of data you want in your dropdown, and where it comes from, your list of SelectListItem's becomes easier to work with in your application.
Published at DZone with permission of Jonathan Danylko, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Future of Software Development: Generative AI Augmenting Roles and Unlocking Co-Innovation
-
Stack in Data Structures
-
DevOps vs. DevSecOps: The Debate
-
Personalized Code Searches Using OpenGrok
Comments