Skip to content

Instantly share code, notes, and snippets.

@kamranayub
Created April 7, 2011 13:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kamranayub/907780 to your computer and use it in GitHub Desktop.
Save kamranayub/907780 to your computer and use it in GitHub Desktop.
MVC FieldLabelFor helper. Helps generate a better label based entirely off a model's metadata properties.
/* add using statements */
public static class HtmlExtensions {
/// <summary>
/// Generates a better label.
/// Text based off given labelText, [DisplayName], or property name.
/// If the field is optional ([Required]), adds an (optional) em tag.
/// If the field has a description ([Description]), adds a class="note" span tag.
/// </summary>
/// <typeparam name="TModel"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="html"></param>
/// <param name="expression"></param>
/// <returns></returns>
/// <remarks>
/// Output markup:
/// <code><![CDATA[
/// <label for="input">
/// [Label text]
/// <em class="optional">(optional)</em>
/// <span class="note">A description</span>
/// </label>
/// ]]>
/// </code>
/// Inspired by: http://stackoverflow.com/questions/3149385/can-i-change-the-way-labelfor-render-in-mvc/3153654#3153654
/// </remarks>
public static MvcHtmlString FieldLabelFor<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression, string labelText = null) {
string propName = ExpressionHelper.GetExpressionText(expression);
string unqualifiedPropName = propName.Split('.').Last(); // if there is a . in the name, take the rightmost part.
ModelMetadata metadata = html.ViewData.ModelMetadata.Properties.First(p => p.PropertyName == propName);
string finallabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? unqualifiedPropName;
if (String.IsNullOrEmpty(finallabelText))
{
return MvcHtmlString.Empty;
}
StringBuilder htmlBuilder = new StringBuilder();
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(propName));
htmlBuilder.Append(finallabelText);
if (!metadata.IsRequired)
{
htmlBuilder.Append(" <em class='optional'>(optional)</em>");
}
// Check description in metadatatype too
var metaDataType = metadata.ContainerType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
var description = metadata.ContainerType.GetProperty(unqualifiedPropName)
.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
// No description attr on model prop, check metadatatype
if (description == null && metaDataType != null)
{
var metaProp = ((MetadataTypeAttribute)metaDataType)
.MetadataClassType.GetProperty(unqualifiedPropName);
if (metaProp != null)
description = metaProp.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
}
if (description != null)
{
htmlBuilder.Append(String.Format("<span class=\"note\">{0}</span>", (description as DescriptionAttribute).Description));
}
tag.InnerHtml = htmlBuilder.ToString();
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment