Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to sort a DropDownList in MVC3 / Razor using helper method

Hi so I'm pretty new to MVC3 and Razor and I've been trying to get my head around it the past few days. I've been given a task by my project architect to create a helper method that sorts a drop down list in an MVC View. I have a View that retrieves various data from a Controller and I'm returning some values that I want to appear in a drop down list. I've been told not to sort it in the Controller and also to pass the field that we want to sort by into the helper method. I could do it like below but the architect wants to keep the view free of c sharp code:

@Html.DropDownListFor(model => model.StudyName, new SelectList(ViewBag.StudyTypes, "Value", "Text").OrderBy(l => l.Text))

So I've created some sample code and some extension methods to try and get it to work. My idea is to replicate the existing Html.DropDownList method and allow the passing of 'object htmlAttributes' so I can set the style as part of the method call.

Here's my code so far. I'm returning the data for the drop down in ViewBag.StudyTypes in the Edit Controller method:

public ActionResult Edit(int id)
{
    IEnumerable<SelectListItem> mySelectList = new List<SelectListItem>();
    IList<SelectListItem> myList = new List<SelectListItem>();

    for (int i = 0; i < 5; i++)
    {
        myList.Add(new SelectListItem() 
            { Value = i.ToString(), Text = "My Item " + i.ToString(), Selected = i == 2 }
        );
    }

    mySelectList = myList;
    ViewBag.StudyTypes = mySelectList;

    StudyDefinition studydefinition = db.StudyDefinitions.Find(id);

    return View(studydefinition);
}

Here's my View code:

    @model MyStudyWeb.Models.StudyDefinition

@using MyStudyWeb.Helpers

@{
    ViewBag.Mode = "Edit";
}
<div>
    @Html.DropDownListSorted(new SelectList(ViewBag.StudyTypes, "Value", "Text"))<br />
    @Html.DropDownListSorted("MyList", new SelectList(ViewBag.StudyTypes, "Value", "Text"))<br />
</div>

Finally below are the extension methods I'm trying to get to work. The first extension method does nothing, I just get a blank space at that point in the View. The second method kind of works but it's ugly. For the 3rd method I don't know how to specify an 'order by' parameter as the OrderBy on an IEnumerable expects a Linq expression.

namespace StudyDefinition.Helpers
{
    public static class HtmlHelperExtensions
    {
        // 1st sort method: sort the passed in list and return a new sorted list
        public static SelectList DropDownListSorted(this HtmlHelper helper, IEnumerable<SelectListItem> selectList)
        {
            var x = new SelectList(selectList.ToList()).OrderBy(l => l.Text);

            return x as SelectList;
        }

        // 2nd sort method: return IHtml string and create <select> list manually
        public static IHtmlString DropDownListSorted(this HtmlHelper helper, string name, SelectList selectList)
        {
            StringBuilder output = new StringBuilder();
            (selectList).OrderBy(l => l.Text);

            output.Append("<select id=" + name + " name=" + name + ">");

            foreach (var item in selectList)
            {
                output.Append("<option value=" + item.Value.ToString() + ">" + item.Text + "</option>");
            }

            output.Append("</select>");

            return MvcHtmlString.Create(output.ToString());
        }

        // 3rd sort method: pass in order by parameter - how do I use this?
        public static IHtmlString DropDownListSorted(this HtmlHelper helper, string name, SelectList selectList, string orderBy)
        {
            StringBuilder output = new StringBuilder();

            //How do I use the orderBy parameter?
            (selectList).OrderBy(l => l.Text);

            output.Append("<select id=" + name + " name=" + name + ">");

            foreach (var item in selectList)
            {
                output.Append("<option value=" + item.Value.ToString() + ">" + item.Text + "</option>");
            }

            output.Append("</select>");

            return MvcHtmlString.Create(output.ToString());
        }        
    }
}

I really don't know the best approach to take, there may be a much simpler way that I'm totally missing and I might be at the point where I can't see the wood for the trees anymore. Some questions

  • Should I return a SelectList or an MvcHtmlString, or something else entirely?

  • For the first extension method how do I get the returned SelectList to render in the View?

  • How to I pass in a parameter to my extension methods that specifies the sort order?

  • How do I pass an 'object htmlAttributes' parameter, and how do I apply this object / parameter to the SelectList?

If anyone has some ideas or suggestions then I'd appreciate some feedback :)

like image 679
Ciarán Bruen Avatar asked Aug 28 '11 19:08

Ciarán Bruen


1 Answers

The first and most important part of your code would be to get rid of any ViewBag/ViewData (which I personally consider as cancer for MVC applications) and use view models and strongly typed views.

So let's start by defining a view model which would represent the data our view will be working with (a dropdownlistg in this example):

public class MyViewModel
{
    public string SelectedItem { get; set; }
    public IEnumerable<SelectListItem> Items { get; set; }
}

then we could have a controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            // I am explicitly putting some items out of order
            Items = new[]
            {
                new SelectListItem { Value = "5", Text = "Item 5" },
                new SelectListItem { Value = "1", Text = "Item 1" },
                new SelectListItem { Value = "3", Text = "Item 3" },
                new SelectListItem { Value = "4", Text = "Item 4" },
            }
        };
        return View(model);
    }
}

and a view:

@model MyViewModel
@Html.DropDownListForSorted(
    x => x.SelectedItem, 
    Model.Items, 
    new { @class = "foo" }
)

and finally the last piece is the helper method which will sort the dropdown by value (you could adapt it to sort by text):

public static class HtmlExtensions
{
    public static IHtmlString DropDownListForSorted<TModel, TProperty>(
        this HtmlHelper<TModel> helper, 
        Expression<Func<TModel, TProperty>> expression, 
        IEnumerable<SelectListItem> items, 
        object htmlAttributes
    )
    {
        var model = helper.ViewData.Model;
        var orderedItems = items.OrderBy(x => x.Value);
        return helper.DropDownListFor(
            expression, 
            new SelectList(orderedItems, "Value", "Text"), 
            htmlAttributes
        );
    }
}
like image 60
Darin Dimitrov Avatar answered Oct 14 '22 05:10

Darin Dimitrov