Skip to main content

Implementation of Dropdown Functionality

1. Description

Dropdown functionality provides users with selectable options in a compact interface. In ASP.NET Core, dropdowns can be static (hardcoded options), dynamic (database-driven), cascading (dependent selections), or enhanced with search and multi-select capabilities. They are essential for data entry forms, filtering, and navigation.

2. Why It Is Important

Dropdowns improve user experience by providing predefined choices, reducing input errors, and ensuring data consistency. They enable structured data entry, facilitate filtering and searching, and create intuitive interfaces for complex relationships between data entities.

3. Real-World Examples

  • Student enrollment forms with course and department dropdowns
  • E-commerce product filters with category, brand, and price range dropdowns
  • HR systems with employee department, role, and location dropdowns
  • Healthcare applications with doctor specialization, hospital, and appointment time dropdowns
  • Inventory management with supplier, category, and warehouse location dropdowns
  • Travel booking with departure city, destination, and airline dropdowns

4. Syntax & Explanation

Models for Dropdown Data

// Base dropdown item model
public class DropdownItem
{
public string Value { get; set; }
public string Text { get; set; }
public bool Selected { get; set; }
public bool Disabled { get; set; }
}

// Enhanced dropdown item with additional properties
public class EnhancedDropdownItem
{
public string Value { get; set; }
public string Text { get; set; }
public string Description { get; set; }
public string Group { get; set; }
public bool Selected { get; set; }
public bool Disabled { get; set; }
public object Data { get; set; } // Additional data payload
}

// Entity models for database-driven dropdowns
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}

public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int DepartmentId { get; set; }
public bool IsActive { get; set; }
public virtual Department Department { get; set; }
public virtual ICollection<Subject> Subjects { get; set; }
}

public class Subject
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int CourseId { get; set; }
public bool IsActive { get; set; }
public virtual Course Course { get; set; }
}

// ViewModel with dropdown properties
public class EnrollmentViewModel
{
// Basic dropdown
public string SelectedDepartment { get; set; }
public List<SelectListItem> Departments { get; set; }

// Cascading dropdowns
public string SelectedCourse { get; set; }
public List<SelectListItem> Courses { get; set; }

public string SelectedSubject { get; set; }
public List<SelectListItem> Subjects { get; set; }

// Multi-select dropdown
public List<string> SelectedSkills { get; set; }
public List<SelectListItem> AvailableSkills { get; set; }

// Grouped dropdown
public string SelectedElective { get; set; }
public List<SelectListGroup> ElectiveGroups { get; set; }

// Searchable dropdown
public string SelectedInstructor { get; set; }
public List<SelectListItem> Instructors { get; set; }

// Other form fields
public string StudentName { get; set; }
public string Email { get; set; }
public DateTime EnrollmentDate { get; set; }
}

Controller with Dropdown Operations

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using YourProjectName.Data;
using YourProjectName.Models;

public class EnrollmentController : Controller
{
private readonly AppDbContext _context;

public EnrollmentController(AppDbContext context)
{
_context = context;
}

// GET: Enrollment/Create - Initialize dropdowns
public async Task<IActionResult> Create()
{
var model = new EnrollmentViewModel
{
EnrollmentDate = DateTime.Today,
Departments = await GetDepartmentsAsync(),
Courses = new List<SelectListItem>(), // Initially empty
Subjects = new List<SelectListItem>(), // Initially empty
AvailableSkills = await GetSkillsAsync(),
ElectiveGroups = await GetElectiveGroupsAsync(),
Instructors = await GetInstructorsAsync()
};

return View(model);
}

// POST: Enrollment/Create - Process form submission
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(EnrollmentViewModel model)
{
if (ModelState.IsValid)
{
// Process enrollment logic here
TempData["Success"] = "Enrollment created successfully!";
return RedirectToAction(nameof(Index));
}

// Repopulate dropdowns on validation failure
model.Departments = await GetDepartmentsAsync(model.SelectedDepartment);
model.Courses = await GetCoursesAsync(model.SelectedDepartment, model.SelectedCourse);
model.Subjects = await GetSubjectsAsync(model.SelectedCourse, model.SelectedSubject);
model.AvailableSkills = await GetSkillsAsync(model.SelectedSkills);
model.ElectiveGroups = await GetElectiveGroupsAsync(model.SelectedElective);
model.Instructors = await GetInstructorsAsync(model.SelectedInstructor);

return View(model);
}

// AJAX: Get courses for selected department
[HttpGet]
public async Task<IActionResult> GetCoursesByDepartment(string departmentId)
{
var courses = await GetCoursesAsync(departmentId);
return Json(courses);
}

// AJAX: Get subjects for selected course
[HttpGet]
public async Task<IActionResult> GetSubjectsByCourse(string courseId)
{
var subjects = await GetSubjectsAsync(courseId);
return Json(subjects);
}

// AJAX: Search instructors
[HttpGet]
public async Task<IActionResult> SearchInstructors(string searchTerm)
{
var instructors = await _context.Instructors
.Where(i => i.IsActive &&
(string.IsNullOrEmpty(searchTerm) ||
i.FirstName.Contains(searchTerm) ||
i.LastName.Contains(searchTerm) ||
i.Email.Contains(searchTerm)))
.OrderBy(i => i.LastName)
.ThenBy(i => i.FirstName)
.Select(i => new SelectListItem
{
Value = i.Id.ToString(),
Text = $"{i.FirstName} {i.LastName} ({i.Department.Name})"
})
.ToListAsync();

return Json(instructors);
}

// Helper methods for dropdown data
private async Task<List<SelectListItem>> GetDepartmentsAsync(string selectedValue = null)
{
return await _context.Departments
.Where(d => d.IsActive)
.OrderBy(d => d.Name)
.Select(d => new SelectListItem
{
Value = d.Id.ToString(),
Text = d.Name,
Selected = d.Id.ToString() == selectedValue
})
.ToListAsync();
}

private async Task<List<SelectListItem>> GetCoursesAsync(string departmentId, string selectedValue = null)
{
if (string.IsNullOrEmpty(departmentId))
return new List<SelectListItem>();

return await _context.Courses
.Where(c => c.IsActive && c.DepartmentId.ToString() == departmentId)
.OrderBy(c => c.Name)
.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = $"{c.Name} ({c.Code})",
Selected = c.Id.ToString() == selectedValue
})
.ToListAsync();
}

private async Task<List<SelectListItem>> GetSubjectsAsync(string courseId, string selectedValue = null)
{
if (string.IsNullOrEmpty(courseId))
return new List<SelectListItem>();

return await _context.Subjects
.Where(s => s.IsActive && s.CourseId.ToString() == courseId)
.OrderBy(s => s.Name)
.Select(s => new SelectListItem
{
Value = s.Id.ToString(),
Text = $"{s.Name} ({s.Code})",
Selected = s.Id.ToString() == selectedValue
})
.ToListAsync();
}

private async Task<List<SelectListItem>> GetSkillsAsync(List<string> selectedValues = null)
{
var allSkills = new List<string>
{
"C#", "Java", "Python", "JavaScript", "React", "Angular", "Node.js",
"SQL", "MongoDB", "Docker", "Kubernetes", "AWS", "Azure", "Git",
"Agile", "Scrum", "Communication", "Leadership", "Problem Solving"
};

return allSkills.Select(skill => new SelectListItem
{
Value = skill,
Text = skill,
Selected = selectedValues != null && selectedValues.Contains(skill)
}).ToList();
}

private async Task<List<SelectListGroup>> GetElectiveGroupsAsync(string selectedValue = null)
{
var groups = new List<SelectListGroup>
{
new SelectListGroup { Name = "Technical Electives" },
new SelectListGroup { Name = "Management Electives" },
new SelectListGroup { Name = "Open Electives" }
};

var items = new List<SelectListItem>
{
// Technical Electives
new SelectListItem { Value = "ai", Text = "Artificial Intelligence", Group = groups[0] },
new SelectListItem { Value = "ml", Text = "Machine Learning", Group = groups[0] },
new SelectListItem { Value = "cyber", Text = "Cybersecurity", Group = groups[0] },
new SelectListItem { Value = "cloud", Text = "Cloud Computing", Group = groups[0] },

// Management Electives
new SelectListItem { Value = "pm", Text = "Project Management", Group = groups[1] },
new SelectListItem { Value = "ba", Text = "Business Analytics", Group = groups[1] },
new SelectListItem { Value = "finance", Text = "Financial Management", Group = groups[1] },

// Open Electives
new SelectListItem { Value = "psychology", Text = "Psychology", Group = groups[2] },
new SelectListItem { Value = "design", Text = "Design Thinking", Group = groups[2] },
new SelectListItem { Value = "entrepreneurship", Text = "Entrepreneurship", Group = groups[2] }
};

// Set selected value
if (!string.IsNullOrEmpty(selectedValue))
{
var selectedItem = items.FirstOrDefault(i => i.Value == selectedValue);
if (selectedItem != null)
selectedItem.Selected = true;
}

return groups;
}

private async Task<List<SelectListItem>> GetInstructorsAsync(string selectedValue = null)
{
return await _context.Instructors
.Where(i => i.IsActive)
.OrderBy(i => i.LastName)
.ThenBy(i => i.FirstName)
.Select(i => new SelectListItem
{
Value = i.Id.ToString(),
Text = $"{i.FirstName} {i.LastName} - {i.Department.Name}",
Selected = i.Id.ToString() == selectedValue
})
.ToListAsync();
}
}

View with Multiple Dropdown Types (Create.cshtml)

@model EnrollmentViewModel
@{
ViewData["Title"] = "New Enrollment";
}

<div class="container">
<div class="row mb-4">
<div class="col">
<h1>New Student Enrollment</h1>
</div>
<div class="col-auto">
<a asp-action="Index" class="btn btn-outline-secondary">Back to List</a>
</div>
</div>

<div class="row">
<div class="col-md-8">
<form asp-action="Create" method="post">
<div asp-validation-summary="ModelOnly" class="alert alert-danger"></div>

<!-- Basic Information -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Student Information</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label asp-for="StudentName" class="form-label"></label>
<input asp-for="StudentName" class="form-control" placeholder="Enter student name" />
<span asp-validation-for="StudentName" class="text-danger"></span>
</div>
<div class="col-md-6 mb-3">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" class="form-control" placeholder="student@example.com" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="col-md-6 mb-3">
<label asp-for="EnrollmentDate" class="form-label"></label>
<input asp-for="EnrollmentDate" class="form-control" type="date" />
<span asp-validation-for="EnrollmentDate" class="text-danger"></span>
</div>
</div>
</div>

<!-- Cascading Dropdowns -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Academic Selection</h5>
</div>
<div class="card-body">
<div class="row">
<!-- Department Dropdown -->
<div class="col-md-4 mb-3">
<label asp-for="SelectedDepartment" class="form-label">Department</label>
<select asp-for="SelectedDepartment" class="form-select" id="departmentDropdown">
<option value="">-- Select Department --</option>
@foreach (var dept in Model.Departments)
{
<option value="@dept.Value" selected="@dept.Selected">@dept.Text</option>
}
</select>
<span asp-validation-for="SelectedDepartment" class="text-danger"></span>
</div>

<!-- Course Dropdown (Cascading) -->
<div class="col-md-4 mb-3">
<label asp-for="SelectedCourse" class="form-label">Course</label>
<select asp-for="SelectedCourse" class="form-select" id="courseDropdown" disabled>
<option value="">-- Select Course --</option>
@foreach (var course in Model.Courses)
{
<option value="@course.Value" selected="@course.Selected">@course.Text</option>
}
</select>
<span asp-validation-for="SelectedCourse" class="text-danger"></span>
</div>

<!-- Subject Dropdown (Cascading) -->
<div class="col-md-4 mb-3">
<label asp-for="SelectedSubject" class="form-label">Subject</label>
<select asp-for="SelectedSubject" class="form-select" id="subjectDropdown" disabled>
<option value="">-- Select Subject --</option>
@foreach (var subject in Model.Subjects)
{
<option value="@subject.Value" selected="@subject.Selected">@subject.Text</option>
}
</select>
<span asp-validation-for="SelectedSubject" class="text-danger"></span>
</div>
</div>
</div>
</div>

<!-- Advanced Dropdown Features -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Additional Options</h5>
</div>
<div class="card-body">
<div class="row">
<!-- Multi-select Dropdown -->
<div class="col-md-6 mb-3">
<label class="form-label">Technical Skills</label>
<select id="skillsDropdown" class="form-select" multiple>
@foreach (var skill in Model.AvailableSkills)
{
<option value="@skill.Value" selected="@skill.Selected">@skill.Text</option>
}
</select>
<small class="form-text text-muted">Hold Ctrl/Cmd to select multiple skills</small>
<input type="hidden" asp-for="SelectedSkills" id="selectedSkillsInput" />
</div>

<!-- Grouped Dropdown -->
<div class="col-md-6 mb-3">
<label asp-for="SelectedElective" class="form-label">Elective Subject</label>
<select asp-for="SelectedElective" class="form-select">
<option value="">-- Select Elective --</option>
@foreach (var group in Model.ElectiveGroups)
{
<optgroup label="@group.Name">
@foreach (var item in Model.AvailableSkills.Where(s => s.Group?.Name == group.Name))
{
<option value="@item.Value" selected="@item.Selected">@item.Text</option>
}
</optgroup>
}
</select>
<span asp-validation-for="SelectedElective" class="text-danger"></span>
</div>
</div>

<div class="row">
<!-- Searchable Dropdown -->
<div class="col-md-12 mb-3">
<label asp-for="SelectedInstructor" class="form-label">Preferred Instructor</label>
<select asp-for="SelectedInstructor" class="form-select" id="instructorDropdown">
<option value="">-- Search and Select Instructor --</option>
@foreach (var instructor in Model.Instructors)
{
<option value="@instructor.Value" selected="@instructor.Selected">@instructor.Text</option>
}
</select>
<span asp-validation-for="SelectedInstructor" class="text-danger"></span>
</div>
</div>
</div>
</div>

<!-- Form Actions -->
<div class="d-flex justify-content-end">
<a asp-action="Index" class="btn btn-outline-secondary me-2">Cancel</a>
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Complete Enrollment
</button>
</div>
</form>
</div>

<div class="col-md-4">
<!-- Help Section -->
<div class="card">
<div class="card-header">
<h6 class="mb-0">Enrollment Guide</h6>
</div>
<div class="card-body">
<h6>Selection Process:</h6>
<ol class="small">
<li>Select Department first</li>
<li>Choose Course from available options</li>
<li>Select Subject specialization</li>
<li>Add technical skills</li>
<li>Choose elective subject</li>
<li>Preferred instructor (optional)</li>
</ol>

<h6 class="mt-3">Dropdown Features:</h6>
<ul class="small">
<li><strong>Cascading:</strong> Options depend on previous selections</li>
<li><strong>Multi-select:</strong> Choose multiple skills</li>
<li><strong>Grouped:</strong> Electives organized by category</li>
<li><strong>Searchable:</strong> Type to find instructors</li>
</ul>

<div class="alert alert-info small mt-3">
<strong>Note:</strong> Fields marked with * are required.
</div>
</div>
</div>
</div>
</div>
</div>

@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}

<script>
// Cascading Dropdowns
const departmentDropdown = document.getElementById('departmentDropdown');
const courseDropdown = document.getElementById('courseDropdown');
const subjectDropdown = document.getElementById('subjectDropdown');

// Department change -> Load Courses
departmentDropdown.addEventListener('change', function() {
const departmentId = this.value;

// Reset dependent dropdowns
courseDropdown.innerHTML = '<option value="">-- Select Course --</option>';
subjectDropdown.innerHTML = '<option value="">-- Select Subject --</option>';
courseDropdown.disabled = !departmentId;
subjectDropdown.disabled = true;

if (departmentId) {
fetch(`/Enrollment/GetCoursesByDepartment?departmentId=${departmentId}`)
.then(response => response.json())
.then(courses => {
courses.forEach(course => {
const option = new Option(course.text, course.value);
courseDropdown.add(option);
});
})
.catch(error => console.error('Error loading courses:', error));
}
});

// Course change -> Load Subjects
courseDropdown.addEventListener('change', function() {
const courseId = this.value;

// Reset subject dropdown
subjectDropdown.innerHTML = '<option value="">-- Select Subject --</option>';
subjectDropdown.disabled = !courseId;

if (courseId) {
fetch(`/Enrollment/GetSubjectsByCourse?courseId=${courseId}`)
.then(response => response.json())
.then(subjects => {
subjects.forEach(subject => {
const option = new Option(subject.text, subject.value);
subjectDropdown.add(option);
});
})
.catch(error => console.error('Error loading subjects:', error));
}
});

// Multi-select Skills Dropdown
const skillsDropdown = document.getElementById('skillsDropdown');
const selectedSkillsInput = document.getElementById('selectedSkillsInput');

// Update hidden input when skills selection changes
skillsDropdown.addEventListener('change', function() {
const selectedOptions = Array.from(this.selectedOptions);
const selectedValues = selectedOptions.map(option => option.value);
selectedSkillsInput.value = JSON.stringify(selectedValues);
});

// Initialize selected skills on page load
document.addEventListener('DOMContentLoaded', function() {
const selectedSkills = @Html.Raw(Model.SelectedSkills != null ? JsonSerializer.Serialize(Model.SelectedSkills) : "[]");
selectedSkillsInput.value = JSON.stringify(selectedSkills);

// Set selected state in dropdown
Array.from(skillsDropdown.options).forEach(option => {
if (selectedSkills.includes(option.value)) {
option.selected = true;
}
});
});

// Searchable Instructor Dropdown
const instructorDropdown = document.getElementById('instructorDropdown');
let allInstructors = [];

// Store all instructor options on page load
document.addEventListener('DOMContentLoaded', function() {
allInstructors = Array.from(instructorDropdown.options).map(option => ({
value: option.value,
text: option.text
}));
});

// Add search functionality
instructorDropdown.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();

// Clear and repopulate based on search
this.innerHTML = '<option value="">-- Search and Select Instructor --</option>';

const filteredInstructors = allInstructors.filter(instructor =>
instructor.text.toLowerCase().includes(searchTerm)
);

filteredInstructors.forEach(instructor => {
const option = new Option(instructor.text, instructor.value);
this.add(option);
});
});

// Form validation feedback
const form = document.querySelector('form');
form.addEventListener('submit', function(e) {
let isValid = true;

// Custom validation for cascading dropdowns
if (!departmentDropdown.value) {
showError(departmentDropdown, 'Please select a department');
isValid = false;
} else {
clearError(departmentDropdown);
}

if (!courseDropdown.value) {
showError(courseDropdown, 'Please select a course');
isValid = false;
} else {
clearError(courseDropdown);
}

if (!subjectDropdown.value) {
showError(subjectDropdown, 'Please select a subject');
isValid = false;
} else {
clearError(subjectDropdown);
}

if (!isValid) {
e.preventDefault();
}
});

function showError(element, message) {
element.classList.add('is-invalid');
let feedback = element.parentNode.querySelector('.invalid-feedback');
if (!feedback) {
feedback = document.createElement('div');
feedback.className = 'invalid-feedback';
element.parentNode.appendChild(feedback);
}
feedback.textContent = message;
}

function clearError(element) {
element.classList.remove('is-invalid');
const feedback = element.parentNode.querySelector('.invalid-feedback');
if (feedback) {
feedback.remove();
}
}
</script>
}

Enhanced Dropdown with Bootstrap Select Plugin

<!-- Include Bootstrap Select CSS and JS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/css/bootstrap-select.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/js/bootstrap-select.min.js"></script>

<!-- Enhanced Multi-select with Search -->
<div class="form-group">
<label class="form-label">Advanced Skills Selection</label>
<select class="form-select selectpicker" multiple data-live-search="true"
title="Choose skills..." data-actions-box="true"
data-selected-text-format="count > 2">
<optgroup label="Programming Languages">
<option value="csharp">C#</option>
<option value="java">Java</option>
<option value="python">Python</option>
<option value="javascript">JavaScript</option>
</optgroup>
<optgroup label="Web Technologies">
<option value="react">React</option>
<option value="angular">Angular</option>
<option value="vue">Vue.js</option>
<option value="nodejs">Node.js</option>
</optgroup>
<optgroup label="Databases">
<option value="sql">SQL Server</option>
<option value="mongodb">MongoDB</option>
<option value="postgresql">PostgreSQL</option>
</optgroup>
<optgroup label="Cloud & DevOps">
<option value="aws">AWS</option>
<option value="azure">Azure</option>
<option value="docker">Docker</option>
<option value="kubernetes">Kubernetes</option>
</optgroup>
</select>
</div>

<script>
// Initialize Bootstrap Select
$('.selectpicker').selectpicker();

// Handle selection changes
$('.selectpicker').on('changed.bs.select', function (e, clickedIndex, isSelected, previousValue) {
const selected = $(this).val();
console.log('Selected items:', selected);
// Update hidden input or perform other actions
});
</script>

5. Use Cases

  • Student Enrollment: Department → Course → Subject cascading selection
  • E-commerce Filters: Category → Subcategory → Brand → Price range filtering
  • HR Management: Country → State → City location selection
  • Healthcare Systems: Hospital → Department → Doctor → Appointment time
  • Inventory Management: Warehouse → Category → Product → Variant selection
  • Travel Booking: Origin → Destination → Airline → Class selection
  • Survey Forms: Multi-select skills, grouped categories, searchable options

6. Mini Practice Task

  1. Basic Dropdown Implementation:

    • Create static dropdowns for gender, status, or priority levels
    • Implement database-driven dropdown for categories or departments
    • Add form validation for required dropdown selections
  2. Cascading Dropdowns:

    • Implement Country → State → City cascading dropdowns
    • Add Category → Subcategory → Product cascading selection
    • Include proper loading states and error handling
  3. Advanced Dropdown Features:

    • Create searchable dropdown with AJAX data loading
    • Implement multi-select dropdown with Bootstrap Select plugin
    • Add grouped dropdowns with optgroups
    • Create custom dropdown with image and description support
    • Implement dropdown with checkbox selection for multiple items