Category and Sub Category in single Drop Down List (DDL) in MVC
In
this article you will learn how to create a Drop Down List in MVC that can
display States and its associated Districts in a single Drop Down List. You
will learn this step by step.
Introduction
Before
talking about anything, let’s look at the image.
In
above DDL, you can see we managed to display States and its associated Districts
in a single DDL. However, you can achieve this using two different DDL, one for
‘States’ and another for ‘Districts’ and once ‘State’ is selected, populated
the ‘District’ DDL with matching items. Here is one article Cascading
DropDownList in ASP.NET MVC I published long back that explains this.
Now, let’s talk
about what we are going to build or achieve.
1.
User can’t select ‘State’, because it is disabled, you can only select
‘Districts’.
2.
Unobtrusive validation should work on button click.
3.
On button click, if validation passed, it should display which state and
district where selected.
4.
You should always keep in mind while applying style on select/option (DDL), except
background-color and color, style settings applied through the style object for
the option element are ignored.
5. We disabled and applied style in each ‘State’ using simple jQuery trick.
Now,
let’s go ahead and follow the simple steps to create this.
Step 1: Create
Application and Add Models
Create
simple MVC Application using regular templates and also add these domain
models.
public class State
{
public int Id { get; set; }
public string StateName { get; set; }
public virtual ICollection<District> Districts { get; set; }
}
public class District
{
public int Id { get; set; }
public string DistrictName { get; set; }
public int StateId { get; set; }
public virtual State State { get; set; }
}
So, we created two different model classes State and District and added above properties. If you look closely we have
established one to many relationship between both domain models. This can be
defined like, for each ‘District’ there will be a single State and for each State there will be
collection of District.
Step 2: Create
Controller and Scaffold the CRUD views
Right
click on Controller to select | Add | Scaffold. In the ‘Add Scaffold’ dialog
select ‘MVC 5 Controller with views, using Entity Framework and hit Add. Now,
you will get following dialog, where you need to select necessary model class
and Data context class.
Click
on Add in above dialog and it will create CRUD views as well as Controller and
DbContext. Now, repeat this to create CRUD for District model class.
Step 3: Insert
sample data for District and State
Once
you have CRUD views for State and District, you can insert sample data, as given below.
Step 4: Create
ViewModel
Now,
let’s create Category and Sub Category in a single drop down list (DDL) in MVC.
We could go without any ViewModel, but as we talked earlier we will be validating
data and also going to do POST, so we need it. Even, this will help reader to
understand it and use the code without much changes. So, this ViewModel is
going to be very simple by name StateDistrictViewModel.
public class StateDistrictViewModel
{
public int Id { get; set; }
[Required]
[Display(Name = "Select
District")]
public string SelectedStateDistrict { get; set; }
}
In
the above code, we marked SelectedStateDistrict field as required
and also modified its Display Name.
Step 5: GET
ActionResult method
We want to display this DDL on Index view of Home Controller, so let’s update
Index action, so that it returns list of States and Districts in
arranged/ordered format.
public ActionResult Index()
{
ViewBag.StateDistrictList = new SelectList(getStateDistrictList(),
"Id", "Column1");
return View();
}
Preceding
code is using ViewBag StateDistrictList to send data to
view page. One more thing that we're using a method call getStateDistrictList() instead of IEnumerable collection item.
This is because, we need a little complex query to get the IEnumerable
collection, that’s why I thought to put this in a separate method, and this
method will return list of items of type IEnumerable (yellow highlighted), here
is the method.
private System.Collections.IEnumerable
getStateDistrictList()
{
//SELECT
' -> ' + Districts.DistrictName, Districts.Id, Districts.StateId FROM
Districts UNION
SELECT States.StateName, -1 , States.Id FROM States ORDER BY
Districts.StateId,Districts.Id
return (
from Districts in db.Districts
select new
{
Column1 = (" -> " +
Districts.DistrictName),
Id = Districts.Id,
StateId = Districts.StateId
}
).Union
(
from States in db.States
select new
{
Column1 = States.StateName,
Id = (-1),
StateId = States.Id
}
).OrderBy(p =>
p.StateId).ThenBy(p => p.Id).ToList();
}
In
the preceding code, you can see we marked SQL code as comment, this is actually
LINQ equivalent code that we have used with return keyword. This code/query will
return the unified ordered and shorted data that DDL will consume. See how
similar it looks in LINQPad when we execute above SQL query. This is ditto output,
look at the image at the top in this post.
In
the image, you can see output has three columns, first by name ‘Column1’ and
that’s why we have used ‘Column1’ with SelectList(getStateDistrictList(), "Id", "Column1"),
yellow highlighted code.
Another
thing to note in above image, for each State, Id is marked as -1 intentionally
because this will be used to find which item to disable in the list hence user
will not select state. That’s why we're using ‘Id’ as data value field with SelectList(getStateDistrictList(),
"Id", "Column1"), yellow
highlighted code.
Step 6: POST ActionResult
method
The
main goal of this method is to display the data (State and District) selected
by user on the view page. So, first we need to grab SelectedStateDistrict from the StateDistrictViewModel and store it in selectedDistrictId. Now use selectedDistrictId to find out the DistrictName and StateName and then attach it
with ViewBag.Message.
[HttpPost]
public ActionResult Index(StateDistrictViewModel model)
{
int selectedDistrictId = Convert.ToInt32(model.SelectedStateDistrict);
//query
to get District
var districtname = db.Districts.Where(x => x.Id ==
selectedDistrictId).Select(x => x.DistrictName).FirstOrDefault();
//query
to get State
var statename = db.Districts.Where(x => x.Id ==
selectedDistrictId).Select(x => x.State.StateName).FirstOrDefault();
ViewBag.StateDistrictList = new SelectList(getStateDistrictList(),
"Id", "Column1");
ViewBag.Message = "You have selected State = " + statename + "
and District = " + districtname;
return View();
}
We have also used ViewBag.StateDistrictList which will let the
DDL filled with data so that user can reselect item. If you don’t use it, will
throw an error.
Now,
let’s understand how to disable selection on the State names.
Step 7: Disable
State name selection
Here we're using jQuery to loop through each list items and if any list item has value
set to ‘-1’, disable it and add color and background-color using .addClass
jQuery method. Make sure to use the reference of jQuery library here.
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<style>
.states {
color: #00ff21;
background-color:#808080;
}
</style>
<script>
$('option').each(function () {
var name = $(this).attr("value");
if (name == "-1") {
$(this).attr("disabled", "true");
$(this).addClass("states");
}
});
</script>
Now,
let’s understand how to create DropDownList using Razor syntaxes that will
render HTML list/option on the view page and also it will consume ViewBag.StateDistrictList.
Step 8: DDL on View
page
Here
is the Razor DDL which we will be using, to render HTML list/option.
@Html.DropDownList("SelectedStateDistrict", ViewBag.StateDistrictList as SelectList, "Select District")
@ViewBag.Message
We're also using ViewBag.Message, just to display
what state and district where selected by the user.
Now,
let’s run the application you will see everything in action.
You
can also find the entire code on GitHub here.
Thanks
for reading.
Comments
Post a Comment