daanl
daanl

Reputation: 44

c# MVC Html helper + expressions + remove the uglyness

Im trying to create an dropdownlist from an IList with following syntax:

@Html.DropDowntListFor(Model.VisitingAddresses, vistingAddress => vistingAddress.Id, vistingAddress => vistingAddress.Name)

This works with the following code:

public static IHtmlString DropDowntListFor
        <TModel>(this HtmlHelper htmlHelper, IList<TModel> list, Expression<Func<TModel, string>> value, Expression<Func<TModel, string>> text)
    {
        var dropdownName = value.Parameters.First().Name;

        var selectedListItem = new List<SelectListItem>();

        var values = list.AsQueryable().Select(value).ToList();
        var texts = list.AsQueryable().Select(text).ToList();

        int i;
        for (i = 0; i < values.Count; i++)
        {
            selectedListItem.Add(new SelectListItem
                                     {
                                        Value = values[i],
                                        Text = texts[i]
                                     });
        }

        return htmlHelper.DropDownList(dropdownName, selectedListItem);
    }

But as you can see the code above (in the htmlhelper) is really really ugly, is there someone that knows an more beautiful way (in code) for in the html helper?

Thanks in advance.

Upvotes: 3

Views: 1710

Answers (2)

Darin Dimitrov
Darin Dimitrov

Reputation: 1039110

How about the following:

@Html.DropDowntListForVisitingAddress(x => x.Id, x => x.Name)

and then because your view is already strongly typed to some model, HtmlHelper is already strongly typed => use this to fetch the model:

public static IHtmlString DropDowntListForVisitingAddress(
    this HtmlHelper<MyViewModel> html,
    Func<VisitingAddress, string> value,
    Func<VisitingAddress, string> text
)
{
    MyViewModel model = html.ViewData.Model;
    var values = model.VisitingAddresses.Select(x => new SelectListItem
    {
        Value = value(x),
        Text = text(x)
    });
    var selectList = new SelectList(values, "Value", "Text");
    return html.DropDownListFor(
        x => x.SelectedAddress,
        selectList
    );
}

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1502076

Are you after something like this?

public static IHtmlString DropDowntListFor<TModel>
    (this HtmlHelper htmlHelper, IList<TModel> list, 
     Expression<Func<TModel, string>> valueSelector, 
     Expression<Func<TModel, string>> textSelector)
{
    var dropdownName = valueSelector.Parameters.First().Name;

    Func<TModel, string> compiledValueSelector = valueSelector.Compile();
    Func<TModel, string> compiledTextSelector = textSelector.Compile();

    var selectedListItem = list.Select(x => new SelectListItem {
                                           Value = compiledValueSelector(x),
                                           Text = compiledTextSelector(x) })
                               .ToList();

    return htmlHelper.DropDownList(dropdownName, selectedListItem);
}

Note that if you don't need the text selector as an expression tree, you can simplify it slightly further:

public static IHtmlString DropDowntListFor<TModel>
    (this HtmlHelper htmlHelper, IList<TModel> list, 
     Expression<Func<TModel, string>> valueSelectorExpression, 
     Func<TModel, string> textSelector)
{
    var dropdownName = valueSelector.Parameters.First().Name;

    var valueSelector = valueSelectorExpression.Compile();

    var selectedListItem = list.Select(x => new SelectListItem {
                                           Value = valueSelector(x),
                                           Text = textSelector(x) })
                               .ToList();

    return htmlHelper.DropDownList(dropdownName, selectedListItem);
}

Upvotes: 4

Related Questions