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

Popular posts from this blog

Migrating database from ASP.NET Identity to ASP.NET Core Identity

Customize User's Profile in ASP.NET Identity System