Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using Dropdowns in Grids

DZone's Guide to

Using Dropdowns in Grids

In this post, we learn how to use postbacks and WebGrid to create dynamic dropdowns, by creating an application that allows you to compare different car models.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

In today's post, we examine a way to perform automatic postbacks along with using dropdowns inside a WebGrid.

Test Data

We need some test data for our specific dropdown and WebGrid.

I decided to grab a GitHub repository of over 7,000 vehicle makes and models by year from N8barr (Thanks, Nate), but don't worry, we are only using about 800 records for our test.

This test data (800 records only) was imported into a new table called VehicleModelYear (in app_data).

Now that we have our test data, we can start experimenting with dropdowns and WebGrids.

Laying the Foundation

I will create a Vehicle repository, ViewModel, and Model for our example as I did before with our MonthData from the previous dropdown example.

Repository/VehicleRepository.cs

public class VehicleRepository : AdoRepository<Vehicle>
{
    public VehicleRepository(string connectionString) 
        : base(connectionString) { }
    public IEnumerable<Vehicle> GetAll()
    {
        using (var command = new 
            SqlCommand("SELECT Id, Year, Make, Model FROM VehicleModelYear"))
        {
            return GetRecords(command);
        }
    }
    public Vehicle GetById(string id)
    {
        using (var command = new 
            SqlCommand("SELECT Id, Year, Make, Model FROM VehicleModelYear WHERE Id = @id"))
        {
            command.Parameters.Add(new ObjectParameter("id", id));
            return GetRecord(command);
        }
    }    public override Vehicle PopulateRecord(SqlDataReader reader)
    {
        return new Vehicle
        {
            Id = reader.GetInt32(0),
            Year = reader.GetInt32(1),
            Make = reader.GetString(2),
            Model = reader.GetString(3)
        };
    }
}

ViewModels/VehicleViewModel.cs

public class VehicleViewModel
{
    public IEnumerable<Vehicle> AllVehicles { get; set; }
    public int SelectedYear { get; set; }
    public IEnumerable<Vehicle> SelectedVehicles { get; set; }
    public IEnumerable<SelectListItem> GetVehicleYearSelectList(int defaultYear = 0)
    {
        return AllVehicles
            .Distinct(new VehicleYearComparer())
            .OrderBy(e => e.Year)
            .Select((e, i) => new SelectListItem
            {
                Text = e.Year.ToString(),
                Value = e.Year.ToString(),
                Selected = e.Year == defaultYear
            });
    }
}
public class VehicleYearComparer : IEqualityComparer<Vehicle>
{
    public bool Equals(Vehicle x, Vehicle y)
    {
        return x.Year.Equals(y.Year);
    }
    public int GetHashCode(Vehicle obj)
    {
        return obj.Year.GetHashCode();
    }
}

Models/Vehicle.cs

public class Vehicle
{
    public int Id { get; set; }
    public int Year { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

As you can see in our VehicleViewModel, we have two types of IEnumerable vehicles: All of the vehicles and the SelectedVehicles.

The SelectedVehicles are the filtered vehicles by year. We could apply caching to the AllVehicles so we aren't hitting the database so often.

We've also added an IEqualityComparer<Vehicle> for creating a Distinct list of years in chronological order, and, finally, we created a simple IEnumerable<SelectListItem> out of them.

All ready for the View (no, not the TV show).

Building the View

Our View is made up of minimal JavaScript and some C# code.

The goal here is to have a list of years in the dropdown and as a user selects one from the dropdown, it will automatically submit the form ("postback") and prepare our filtered list of cars for our selected year.

Here is what our View looks like.

Views/Home/WebGrid.cshtml

@{
    ViewBag.Title = "WebGrid Dropdown";
    var grid = new WebGrid(Model.SelectedVehicles.OrderBy(e=> e.Make), canPage: false);
}
@model DropDownDemo.ViewModels.VehicleViewModel
<h3>WebGrid Example</h3>
@@using (Html.BeginForm("WebGrid", "Home", FormMethod.Post, new { @@class = "vehicle-form form-horizontal" }))
{
    <div class="form-group">
        @@Html.Label("Year", "Year:", new { @@class = "col-sm-1 control-label" })
        <div class="col-sm-2">
            @@Html.DropDownListFor(model => model.SelectedYear,
                Model.GetVehicleYearSelectList(),
                new { @class = "vehicle-year form-control" })
        </div>
    </div>
    @@MvcHtmlString.Create(
        grid.GetHtml(
            tableStyle: "table table-bordered table-striped table-condensed",
            htmlAttributes: new {
                id = "grid"
            },
            columns: grid.Columns(
                grid.Column("Make" , "Make"),
                grid.Column("Model", "Model")
            )).ToHtmlString()
        )
}
<script>
    document.getElementsByClassName('vehicle-year')[0].onchange = function () {
        document.getElementsByClassName('vehicle-form')[0].submit();
    };
</script>

At the bottom of the View, we have our JavaScript (mind you, no jQuery, but you could use it) submit the form ("vehicle-form") when a user changes the "vehicle-year" dropdown.

This form's submit will perform a postback, try to create a new VehicleViewModel based on the data it has available in the View, and send the VehicleViewModel over to the POST of the WebGrid in the controller.

Controller Code

The controller code is pretty simple.

Controllers/HomeController.cs

..public ActionResult WebGrid()
{
    var model = new VehicleViewModel
    {
        AllVehicles = _vehicleRepository.GetAll()
    };
    // Grab the first year.
    var firstYear = model.GetVehicleYearSelectList().First().Value;
    model.SelectedVehicles = model.AllVehicles.Where(e => e.Year.ToString() == firstYear);
    return View(model);
}
[HttpPost]
public ActionResult WebGrid(VehicleViewModel model)
{
    model.AllVehicles = _vehicleRepository.GetAll();
    model.SelectedVehicles = model.AllVehicles.Where(e => e.Year == model.SelectedYear);
    return View(model);
}

When the controller is called for an initial GET, we need to grab the first year in the sorted SelectList. Once we have that, we can filter out the other vehicles and display only the first year's cars.

When a POST occurs, the model is returned and we only need the SelectedYear to assign the SelectedVehicles to the model.

This makes our cycle of displaying yearly cars even easier.

Dropdown in a WebGrid

So how do we get a Dropdown inside of a WebGrid? Maybe for rating a particular vehicle?

The rating control will be a dropdown with a list of possible ratings from 1 (Poor) to 5 (Excellent).

For our rating to work, we need to modify our WebGrid (in our WebGrid.cshtml) a bit by adding a new column.

@@MvcHtmlString.Create(
    grid.GetHtml(
        tableStyle: "table table-bordered table-striped table-condensed",
        htmlAttributes: new {
            id = "grid"
        },
        columns: grid.Columns(
            grid.Column("Make" , "Make"),
            grid.Column("Model", "Model"),
            grid.Column("Rating", format: item => Html.RatingDropDown(item.Value as Vehicle))
        )).ToHtmlString()
    )

This new code includes a column containing an HtmlHelper class called a RatingDropDown. It was previously introduced in my WebGrid series.

Our HtmlHelper class creates a dropdown based on the Vehicle ratings.

Helpers/Html/HtmlExtensions.cs

public static class HtmlExtensions
{
    public static HtmlString RatingDropDown(this HtmlHelper helper, Vehicle vehicle)
    {
        var ratings = GetRatingList(vehicle.Rating);
        var formatName = HtmlHelper.GenerateIdFromName(nameof(vehicle.Rating));
        var uniqueId = $"{formatName}_{vehicle.Id}";
        var input = helper.DropDownList(uniqueId, ratings, new { @class = "input-sm" });
        return new HtmlString(input.ToString());
    }
    private static IEnumerable<SelectListItem> GetRatingList(int vehicleRating)
    {
        var ratings = new List<SelectListItem>
        {
            new SelectListItem {Text = "1 - Poor", Value = "1"},
            new SelectListItem {Text = "2 - Fair", Value = "2"},
            new SelectListItem {Text = "3 - Good", Value = "3"},
            new SelectListItem {Text = "4 - Great", Value = "4"},
            new SelectListItem {Text = "5 - Excellent", Value = "5"}
        };
        ratings.ForEach(e=> e.Selected = vehicleRating.ToString() == e.Value);
        return ratings;
    }
}

Once these are in place, we can run the app and we have our rating dropdown on every row.

Conclusion

As you can see, dropdowns become easier to use the more you work with them.

If we wanted to improve this screen, we could do the following:

  • As I mentioned before, we could add a caching routine to the AllVehicles to make the postback even faster.
  • Another method would be to eliminate the postback altogether and perform SignalR or WebAPI calls to retrieve the data and display the grid dynamically.
  • An exercise for the readers would be to save the rating back to the database, again, using either SignalR or WebAPI.

If you are working with a number of controls in a table format, it makes more sense to use tables instead of WebGrids. It'll make things a heck of a lot easier with a for...each loop and table tags.

It all renders the same anyway, right?

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
web dev ,webgrid ,web api ,dropdown ,web application development

Published at DZone with permission of Jonathan Danylko, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}