Creating a Multi-Column Dropdown in ASP.NET MVC

In today's post, we expand further on the suggestion dropdown by replacing it with a grid.

Written by Jonathan "JD" Danylko • Last Updated: • MVC •
Screenshot of grid in dropdown

After my ultimate dropdown series, I've had a couple readers ask how to create a multi-column dropdown, or place a grid inside a dropdown.

I know I talked about Dropdowns in Grids, but what about Grids in Dropdowns.

While you can use an unordered list (UL) to create your dropdown as we did in the Suggestion Dropdown from the series, I feel using the table tags is a better approach and avoids the CSS "acrobatics."

Also, keep in mind, since the suggestion dropdown is so similar to the grid, we'll be using that page and modifying it to meet out grid-y needs.

There are very subtle differences between these two routines and I'll cover as much as I can while updating the GitHub repo.

So let's dive in.

Preparing the Grid

Since we're using a table, we need to provide as much static HTML as possible so we aren't creating HUGE amounts of HTML in our JavaScript.

In our table placeholder, we replace the UL with our TABLE tag in the HTML (changes in bold)

@using (Html.BeginForm("GridDropdown", "Home", FormMethod.Post, new { @class = "suggestion-dropdown form-horizontal" }))
{
    <div class="form-group">
        @Html.Label("SearchLabel", "Cars: ", new { @class = "col-sm-1 control-label" })
        <div class="col-sm-4">
            @Html.TextBox("SearchTerm", string.Empty, new { @class = "search-term form-control" })
            <div class="suggestions hidden">
                <table class="table table-condensed table-striped suggest-grid">
                    <tbody></tbody>
                </table>
            </div>
        </div>
    </div>
}

We only want the essentials for our grid dropdown. If you want to add headers (THEAD) or footers (TFOOT), then this is place to do it, NOT in the JavaScript.

Stylin' the Grid

For you Bootstrap users out there, I'm pretty sure you noticed the Bootstrap classes for the table.

As I mentioned before, there are subtle styles we'll apply to this control.

<style>
    .suggestions {
        border1px solid #CCC;
        background-color#FFF;
        width300px;
        z-index1;
        positionabsolute;
        top30px;
        left15px
    }

    .suggest-grid tr.active td { background-color#777 !importantcolor#FFF }
</style>

Since I've added table-striped to the table, it was hard to see the active item in the list when we moved from row to row.

I added the background-color to be a little more obvious to the user. If you want a different color, this is the place to change the grid's selected color and background.

Finally, the JavaScript

It's surprising this JavaScript is so small and does so much.

if you are unsure about certain parts, I'll refer you to the Suggestion Dropdown post.

$(function() {

    var getSelectedValue = function() {         var activeRow = $("tr.active");         var firstColumn = $("td:nth-child(1)", activeRow).text();         var secondColumn = $("td:nth-child(2)", activeRow).text();         var entry = firstColumn + " (" + secondColumn + ")";         return entry;     }
    $("#SearchTerm")         .on("focusout", function(e) {             $(".suggestions").addClass("hidden");         })         .on("keyup keypress",         function(e) {             var active = $("tr.active", ".suggest-grid");             if (e.which === 27) {                 $(".suggestions").addClass("hidden");             } else if (e.which === 40) {                 if (active.length > 0) {                     var next = $(active).next();                     $(active).removeClass("active");                     $(next).addClass("active");                 } else {                     $("tr:first", ".suggest-grid").addClass("active");                 }             } else if (e.which === 38) {                 if (active.length > 0) {                     var previous = $(active).prev();                     $(active).removeClass("active");                     $(previous).addClass("active");                 } else {                     $("tr:last", ".suggest-grid").addClass("active");                 }             } else if (e.which === 13) {                 e.preventDefault();                 var selectedValue = getSelectedValue();                 $(this).val(selectedValue);                 $(".suggestions").addClass("hidden");                 return false;             } else {                 // We have a good value w/ no special keys.                 var value = $("#SearchTerm").val();                 if (value === "") {                     $(".suggestions").addClass("hidden");                 } else {                     var uri = "/api/search/for/" + value;
                    $(".suggestions").removeClass("hidden");
                    $.getJSON(uri)                         .done(function (data) {                             var grid = $(".suggest-grid");                             $("tbody", grid).empty();                             $.each(data,                                 function (key, value) {                                     var row =                                         "<td>" + value.Make + "</td>" +                                         "<td>" + value.Model + "</td>";                                     $("tbody", grid).append($("<tr></tr>").html(row));                                     // On mouse click, set the value.
                                    $("tr", grid).on("click",                                         function (e) {                                             e.preventDefault();                                             // this = the row (tr)                                             // Grabs the first column's value.                                             var selectedValue = getSelectedValue();                                             $(".search-term").val(selectedValue);                                             $(".suggestions").addClass("hidden");                                         });                                 });                         });                 }             }         }); });

Let's examine the changes.

Starting at the top, since we have two places to accept values (when a user hits enter or clicks a row), I decided to create a getSelectedValue function.

This getSelectedValue function will populate the textbox with what you want. I decided to go with "<Make> (<Model>)" as my final input.

Next, we work on the #SearchTerm textbox.

I added an onFocusOut to the control so when they leave the textbox, the suggestions dropdown is removed (hidden).

In the suggestion dropdown, we set an LI with an ".active" class. This code is no different. Instead, we set a TR to ".active" and it functions the exact same way.

If everything falls through to the bottom, we clear the table's rows ($("tbody",grid).empty()), make the JSON call to the API, build the row if we have results, and attach an onClick event to each row.

Conclusion

In today's post, we've taken the suggestion dropdown code and modified it slightly to display grids using tables instead of single list items from unordered lists.

You can take this code and expand on it by:

  • Adding images to the columns
  • Using id's in a hidden table column to place into a hidden field when a record is selected
  • Building multi-line records

With such little amount of code to create your own custom dropdowns, the ability to attach JavaScript to a simple control can expand the possibilities to your end-users.

Are there other JavaScript custom controls you'd like to see built? Was this built properly? Post your comments below and let's discuss.

ASP.NET 8 Best Practices on Amazon

ASP.NET 8 Best Practices by Jonathan Danylko


Reviewed as a "comprehensive guide" and a "roadmap to excellence" with over 120 Best Practices for ASP.NET Core 8, Jonathan's first book by Packt Publishing explores proven techniques for every phase of the SDLC.

Learn industry-standard concepts to improve your coding, debugging, and deployment of ASP.NET Core websites.

Order now on Amazon.com button

Picture of Jonathan "JD" Danylko

Jonathan "JD" Danylko is an author, web architect, and entrepreneur who's been programming for over 30 years. He's developed websites for small, medium, and Fortune 500 companies since 1996.

He currently works at Insight Enterprises as an Architect.

When asked what he likes to do in his spare time, he replies, "I like to write and I like to code. I also like to write about code."

comments powered by Disqus