Inspired, more than a little, by a blog post over at mvcdiary.com, I decided that I could make my life with bootstrap even easier. I had the idea to create HTML Helpers for MVC that would enable the use of some of the display attributes to add more auto-magic goodness to the page.
My goal was to be able to simply use @Html.BootstrapTextBoxFor(m => m.description) in my markup, but the rendered output would include the bootstrap placeholder, and the title attribute would be set to allow for automatic tooltips on mouse over.
I decided that it would make a lot of sense, to use the “description” value of the display attribute for the tooltip, and the “prompt” value of the same attribute for the placeholder. So I started off — as I usually do — to see if anyone had already done this. Sure enough, I found the blog post, which gave me an incredible head start. The grunt work was already done, it was just assigning the additional properties. Here is the final result (feel free to compare to the original).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public static class BootstrapExtensions { public static MvcHtmlString BootstrappedTextboxFor<TModel, TValue>( this HtmlHelper html, Expression<Func<TModel, TValue>> expression, object htmlAttributes) { var dict = new RouteValueDictionary(htmlAttributes); return html.BootstrappedTextboxFor(expression, dict); } public static MvcHtmlString BootstrappedTextboxFor<TModel, TValue>;( this HtmlHelper html, Expression<Func<TModel, TValue>> expression) { var htmlAttributes = new Dictionary<string, object>(); return html.BootstrappedTextboxFor(expression, htmlAttributes); } public static MvcHtmlString BootstrappedTextboxFor<TModel, TValue>( this HtmlHelper html, Expression<Func<TModel, TValue>> expression, IDictionar<string, object> htmlAttributes) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); string htmlFieldName = ExpressionHelper.GetExpressionText(expression); // My modifications start here... I created these two variables, // added some more of the coalesce operator goodness to get the // prompt (called prompt in attributes, but watermark in the meta- // data for some reason), if it's not there get the display name // then fall back to the property name, and if all else fails, use // the field name. Truly, just a slight deviation from the original. string description = metadata.Description ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last(); string prompt = metadata.Watermark ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last(); if (!String.IsNullOrEmpty(description) && !String.IsNullOrEmpty(prompt)) { if (htmlAttributes == null) { htmlAttributes = new Dictionary<string, object>(); } // Then here I'm adding it to the placeholder and title attributes // of the html output. htmlAttributes.Add("placeholder", prompt ?? description ?? ""); htmlAttributes.Add("title", description ?? ""); } return html.TextBoxFor(expression, htmlAttributes); } } |
The result is exactly my desired result.
Markup:
1 2 |
@Html.LabelFor(m=>m.FirstName) @Html.BootstrappedTextboxFor(m=>m.FirstName, new { @class="form-field-full" } ) @Html.ValidationMessageFor(m=>m.FirstName) @Html.LabelFor(m=>m.LastName) @Html.BootstrappedTextboxFor(m => m.LastName, new { @class="form-field-full" } ) @Html.ValidationMessageFor(m=>m.LastName) |
Model:
1 2 3 4 5 6 7 8 9 10 11 |
/// /// Gets or sets the value provided as the first name for the contact form. /// [Display(Name="First Name", Prompt="First", Description="Your First Name")] public string FirstName { get; set; } /// /// Gets or sets the value provided as the last name for the contact form. /// [Display(Name="Last Name", Prompt="Last", Description="Your Last Name")] public string LastName { get; set; } |
It was all very easy to implement, and extend even further and to other standard helpers, such as the textareafor helper.