Click here to Skip to main content
Licence CPOL
First Posted 2 Feb 2012
Views 3,693
Bookmarked 7 times

MVC HtmlHelper: Text Posting Dropdown List

By | 2 Feb 2012 | Article
The HTML Helper that enables the dropdownlist to post the text selected in it along with the value !!!

Introduction

As you all know the dropdown only posts the value selected but not the text to the server. In my recent project I needed the text selected in the dropdown to be available along with the value selected.

The first thing most of the developers, including me, will think is about adding a hidden field to post the text and set the value of the hidden field on change of the dropdown. Yes, I did the same, but in a more reusable way and I thought of sharing it with you guys….

Using the code

Have a close look at the Html Helper ‘DropDownListFor’,

Html.DropDownListFor((m) => m.UserId, Model.UserCollection, "--please select--", new { @style = "width:330px" })

What we are missing over here? Hmmm.. we have only the provision to specify the model property for the Dropdown value. Yes! That means we need to have one more Expression parameter to specify the text of the dropdown as well. So here goes the signature for our new Html Helper with one additional parameter to key in the text model property.

internal static MvcHtmlString TextPostingDropDownListFor<TModel, TValProperty, TTextProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TValProperty>> valueExpression,
        Expression<Func<TModel, TTextProperty>> textExpression,
        IEnumerable<SelectListItem> selectList,
        string optionLabel,
        IDictionary<string, object> htmlAttributes )
    {

Now as we decided we are going to render one hidden control along with the dropdown to hold the text. What are the attributes required for the hidden field? Yes there you are, Name, Id and Text. Lets get them first…


string textControlName =
            htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName
            (ExpressionHelper.GetExpressionText(textExpression));
        
string textControlId =
            htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId
            (ExpressionHelper.GetExpressionText(textExpression));

object text = ModelMetadata.FromLambdaExpression(textExpression, htmlHelper.ViewData).Model;

Next step is to get the Html for the dropdown added with some custom attributes and class. Why we need these attributes and class? , well, those are used by our jquery script to setup the control.


RouteValueDictionary attributes = new RouteValueDictionary(htmlAttributes);
        if (attributes.ContainsKey("class"))
        {
            attributes["class"] = string.Format("{0} {1}", attributes["class"], "tpddl");
        }
        else
        {
            attributes.Add("class", "tpddl");
        }        
        attributes.Add("tpddl-posting-control-id", textControlId);
        
        MvcHtmlString html = htmlHelper.DropDownListFor(valueExpression, selectList, optionLabel, attributes);

You can see that we have added a class tpddl and an attribute tpddl-posting-control-id with value as the text control id.

Now we need the hidden control..

if (htmlAttributes.ContainsKey("class"))
            htmlAttributes.Remove("class");
        TagBuilder tb = new TagBuilder("input");
        tb.MergeAttributes(htmlAttributes);
        tb.MergeAttribute("name", textControlName);
        tb.MergeAttribute("id", textControlId);
        tb.MergeAttribute("type", "hidden");
        tb.MergeAttribute("value", (text != null) ? text.ToString() : string.Empty);

Lets merge the dropdown and textbox html to get the final result.

string strHtml = string.Format("{0}{1}", html.ToHtmlString(), tb.ToString(TagRenderMode.SelfClosing));

return new MvcHtmlString(strHtml);   

Putting it altogether the final code is as below.

internal static MvcHtmlString TextPostingDropDownListFor<TModel, TValProperty, TTextProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TValProperty>> valueExpression,
        Expression<Func<TModel, TTextProperty>> textExpression,
        IEnumerable<SelectListItem> selectList,
        string optionLabel,
        IDictionary<string, object> htmlAttributes)
    {
        string textControlName =
            htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName
            (ExpressionHelper.GetExpressionText(textExpression));
        
        string textControlId =
            htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId
            (ExpressionHelper.GetExpressionText(textExpression));

        object text = ModelMetadata.FromLambdaExpression(textExpression, htmlHelper.ViewData).Model;        

        RouteValueDictionary attributes = new RouteValueDictionary(htmlAttributes);
        if (attributes.ContainsKey("class"))
        {
            attributes["class"] = string.Format("{0} {1}", attributes["class"], "tpddl");
        }
        else
        {
            attributes.Add("class", "tpddl");
        }        
        attributes.Add("tpddl-posting-control-id", textControlId);
        
        MvcHtmlString html = htmlHelper.DropDownListFor(valueExpression, selectList, optionLabel, attributes);

        if (htmlAttributes.ContainsKey("class"))
            htmlAttributes.Remove("class");
        TagBuilder tb = new TagBuilder("input");
        tb.MergeAttributes(htmlAttributes);
        tb.MergeAttribute("name", textControlName);
        tb.MergeAttribute("id", textControlId);
        tb.MergeAttribute("type", "hidden");
        tb.MergeAttribute("value", (text != null) ? text.ToString() : string.Empty);
        string strHtml = string.Format("{0}{1}", html.ToHtmlString(), tb.ToString(TagRenderMode.SelfClosing));

        return new MvcHtmlString(strHtml);        
    }

Now you can add some public overloads for the convenience of the user.

public static MvcHtmlString TextPostingDropDownListFor<TModel, TProperty, TTextProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> valueExpression,
        Expression<Func<TModel, TTextProperty>> textExpression,
        IEnumerable<SelectListItem> selectList)
    {
        var attributes = new RouteValueDictionary();
        return htmlHelper.TextPostingDropDownListFor(
            valueExpression,
            textExpression,
            selectList,
            null,
            attributes);
    }

    public static MvcHtmlString TextPostingDropDownListFor<TModel, TProperty, TTextProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> valueExpression,
        Expression<Func<TModel, TTextProperty>> textExpression,
        IEnumerable<SelectListItem> selectList,
        string optionLabel)
    {
        var attributes = new RouteValueDictionary();
        return htmlHelper.TextPostingDropDownListFor(
            valueExpression,
            textExpression,
            selectList,
            optionLabel,
            attributes);
    }    

    public static MvcHtmlString CMSTextPostingDropDownListFor<TModel, TProperty, TTextProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> valueExpression,
        Expression<Func<TModel, TTextProperty>> textExpression,
        IEnumerable<SelectListItem> selectList,
        string optionLabel,
        object htmlAttributes)
    {
        var attributes = new RouteValueDictionary(htmlAttributes);
        return htmlHelper.TextPostingDropDownListFor(
            valueExpression, 
            textExpression, 
            selectList, 
            optionLabel, 
            attributes);
    }

The next thing I am gonna do is to add the JQuery script to make the control functional.
Add a script file say, textpostingdropdown.js and add the code below in it.

$(document).ready(function () {
    $(".tpddl").setTextPostingDropdown();
}
jQuery.fn.setTextPostingDropdown = function () {
    $(this).each(function () {             
        $(this).makeDropdownTextPostable();
    });
}

jQuery.fn.makeDropdownTextPostable = function () {
    $(this).bind("change", function () {
        var textcontrolid = $(this).attr("tpddl-posting-control-id");        
        var selText = $(this).find("option:selected").text();
        $("#" + textcontrolid).val(selText);
    });
};

Use the control by specifying the Model property for the dropdown text as mentioned below.

Html.TextPostingDropDownListFor((m) => m.UserId,(m) => m.UserName, Model.UserCollection, "--please select--", new { @style = "width:330px" })

Bingo!! That it, the control is ready to post your text….

But you dont forget to post your comment as well !!! :-)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Russell Aboobacker

Web Developer

India India

Member

Russell Aboobacker is a Software Engineer from India, Currently working in Cognizant, Bangalore as Software Architect. He Enjoys Coding and Sharing his Experiences with the Colleagues and Friends.When he is not coding he enjoys spending time with his Family.
 
If you have any suggestions / Ideas , Share it With me. arusselkm@yahoo.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
SuggestionAlternative PinmemberRupeshKumar8:08 3 Feb '12  
GeneralRe: Alternative PinmemberRussell Aboobacker21:10 4 Feb '12  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 3 Feb 2012
Article Copyright 2012 by Russell Aboobacker
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid