From 72732868c6852faba5479eef5528b8c1a1122744 Mon Sep 17 00:00:00 2001 From: harshgMSFT Date: Thu, 10 Apr 2014 13:54:55 -0700 Subject: [PATCH] Adding DisplayName, Label HtmlHelpers. --- .../MvcSample.Web/Views/Shared/MyView.cshtml | 86 +++++++++++-------- .../Microsoft.AspNet.Mvc.Core.kproj | 2 + .../Rendering/Html/HtmlHelper.cs | 75 ++++++++++++++++ .../Rendering/Html/HtmlHelperOfT.cs | 32 +++++++ .../HtmlHelperDisplayNameExtensions.cs | 36 ++++++++ .../Rendering/HtmlHelperLabelExtensions.cs | 65 ++++++++++++++ .../Rendering/IHtmlHelperOfT.cs | 52 +++++++++++ 7 files changed, 312 insertions(+), 36 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperDisplayNameExtensions.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperLabelExtensions.cs diff --git a/samples/MvcSample.Web/Views/Shared/MyView.cshtml b/samples/MvcSample.Web/Views/Shared/MyView.cshtml index a2f485bec1..24c5019118 100644 --- a/samples/MvcSample.Web/Views/Shared/MyView.cshtml +++ b/samples/MvcSample.Web/Views/Shared/MyView.cshtml @@ -18,9 +18,9 @@ display: none; } - #qux.foo { - color: red; - } + #qux.foo { + color: red; + } } @@ -40,7 +40,7 @@ { return "Hello World"; } - + public Task RenderHelloWorldPartial(User model) { // Imagine this method was a super complex method that was used as a helper method. @@ -55,13 +55,15 @@

Hello @Model.Name! Happy @Model.Age birthday.

-

This value was retrieved asynchronously: @(await AsyncValueRetrieval())

+

This value was retrieved asynchronously: @(await AsyncValueRetrieval())

Partial Async: @await Html.PartialAsync("HelloWorldPartial", Model)

-

Render Partial Async (Custom model): @{ await RenderHelloWorldPartial(new User() +

+ Render Partial Async (Custom model): @{ await RenderHelloWorldPartial(new User() { Name="Bob" - }); - }

+ }); + } +

Book a flight

@@ -87,7 +89,10 @@ + + + +
- + @Html.LabelForModel("ForModel") + + '@Html.DisplayNameForModel()' '@Html.NameForModel()' @@ -98,7 +103,10 @@
- + @Html.Label("Name") + + '@Html.DisplayName("Name")' '@Html.Name("Name")' @@ -109,7 +117,10 @@
- + @Html.LabelFor(m => m.Address) + + '@Html.DisplayNameFor(m => m.Address)' '@Html.NameFor(m => m.Address)' @@ -120,7 +131,10 @@
- + @Html.Label("Anon.Address.Street") + + '@Html.DisplayName("Anon.Address.Street")' '@Html.Name("Anon.Address.Street")' @@ -146,7 +160,7 @@
- + @Html.Label("Name") @Html.TextBox("Name") @@ -154,7 +168,7 @@
- + @Html.LabelFor(m => m.Address, htmlAttributes: new { @class="control-label col-md-2" }) @Html.TextBoxFor(m => m.Address, htmlAttributes: new { @class = "form-control" }) @@ -162,7 +176,7 @@
- + @Html.Label("Alive") @Html.CheckBox("Alive") @@ -173,7 +187,7 @@
- + @Html.LabelFor(m => m.Dependent.Alive) @Html.CheckBoxFor(m => m.Dependent.Alive) @@ -184,7 +198,7 @@
- + @Html.LabelFor(model => model.Password, htmlAttributes: new { @class="control-label col-md-2" }) @Html.PasswordFor(m => m.Password, htmlAttributes: new { @class = "form-control" }) @@ -203,7 +217,7 @@
- + @Html.Label("Anon.Name") @Html.TextBox("Anon.Name") @@ -211,7 +225,7 @@
- + @Html.Label("Anon.Address.Street") @Html.TextBox("Anon.Address.Street", (object)ViewBag.Anon.Address.Street) @@ -219,7 +233,7 @@
- + @Html.Label("Anon.Address.City") @Html.TextBox("Anon.Address.City", value: null, format: "{0} (3)") @@ -227,7 +241,7 @@
- + @Html.Label("Anon.Address.State") @Html.TextBox("Anon.Address.State", value: null, htmlAttributes: new { @class = "form-control" }) @@ -235,16 +249,16 @@
- + @Html.Label("Anon.Address.Country") @Html.TextBox("Anon.Address.Country", value: null, format: "{0} (4)", - htmlAttributes: new { @class = "form-control" }) + htmlAttributes: new { @class = "form-control" })
- + @Html.Label("Password") @Html.Password("Password", "some string") @@ -266,7 +280,7 @@
- + @Html.Label("Name", "Display Name") '@Html.Display("Name")' @@ -274,7 +288,7 @@
- + @Html.Label("Dependent.Name", "Display Dependent.Name") '@Html.Display("Dependent.Name")' @@ -282,7 +296,7 @@
- + @Html.Label("Alive", "Display Alive") '@Html.Display("Alive")' @@ -290,7 +304,7 @@
- + @Html.Label("Dependent.Alive", "Display Dependent.Alive") '@Html.Display("Dependent.Alive")' @@ -298,7 +312,7 @@
- + @Html.Label("Age", "Display Age") '@Html.Display("Age")' @@ -306,7 +320,7 @@
- + @Html.Label("GPA", "Display GPA") '@Html.Display("GPA")' @@ -318,7 +332,7 @@
- + @Html.LabelFor(model => model.Name, "DisplayFor Name") '@Html.DisplayFor(model => model.Name)' @@ -326,7 +340,7 @@
- + @Html.LabelFor(model => model.Dependent.Name, "DisplayFor Dependent.Name") '@Html.DisplayFor(model => model.Dependent.Name)' @@ -334,7 +348,7 @@
- + @Html.LabelFor(model => model.Alive, "DisplayFor Alive") '@Html.DisplayFor(model => model.Alive)' @@ -342,7 +356,7 @@
- + @Html.LabelFor(model => model.Dependent.Alive, "DisplayFor Dependent.Alive") '@Html.DisplayFor(model => model.Dependent.Alive)' @@ -350,7 +364,7 @@
- + @Html.LabelFor(model => model.Age, "DisplayFor Age") '@Html.DisplayFor(model => model.Age)' @@ -358,7 +372,7 @@
- + @Html.LabelFor(model => model.GPA, "DisplayFor GPA") '@Html.DisplayFor(model => model.GPA)' diff --git a/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj b/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj index fa6ec36996..fa0472d5e8 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj +++ b/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj @@ -141,7 +141,9 @@ + + diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index 72f3938a12..b394853184 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -224,6 +224,18 @@ namespace Microsoft.AspNet.Mvc.Rendering templateName, additionalViewData); } + + public HtmlString DisplayName(string expression) + { + var modelMetadata = string.IsNullOrEmpty(expression) ? + ViewData.ModelMetadata : + ExpressionMetadataProvider.FromStringExpression( + expression, + ViewData, + MetadataProvider); + return GenerateDisplayName(modelMetadata, expression); + } + public HtmlString DropDownList(string name, IEnumerable selectList, string optionLabel, object htmlAttributes) @@ -241,6 +253,22 @@ namespace Microsoft.AspNet.Mvc.Rendering return GenerateHidden(metadata: null, name: name, value: value, useViewData: (value == null), htmlAttributes: htmlAttributes); } + + + public HtmlString Label(string expression, string labelText, object htmlAttributes) + { + var modelMetadata = string.IsNullOrEmpty(expression)? + ViewData.ModelMetadata : + ExpressionMetadataProvider.FromStringExpression( + expression, + ViewData, + MetadataProvider); + return GenerateLabel( + modelMetadata, + expression, + labelText, + htmlAttributes); + } public virtual HtmlString Name(string name) { @@ -543,6 +571,23 @@ namespace Microsoft.AspNet.Mvc.Rendering format: null, htmlAttributes: htmlAttributeDictionary); } + + protected virtual HtmlString GenerateDisplayName([NotNull] ModelMetadata metadata, string htmlFieldName) + { + // We don't call ModelMetadata.GetDisplayName here because + // we want to fall back to the field name rather than the ModelType. + // This is similar to how the GenerateLabel get the text of a label. + // TODO: This needs to be updated after ModelMetadata has a DisplayName property + var resolvedDisplayName = metadata.PropertyName; + if (resolvedDisplayName == null) + { + resolvedDisplayName = string.IsNullOrEmpty(htmlFieldName) ? + string.Empty : + htmlFieldName.Split('.').Last(); + } + + return new HtmlString(Encode(resolvedDisplayName)); + } protected HtmlString GenerateDropDown(ModelMetadata metadata, string expression, IEnumerable selectList, string optionLabel, object htmlAttributes) @@ -632,6 +677,36 @@ namespace Microsoft.AspNet.Mvc.Rendering format: null, htmlAttributes: htmlAttributeDictionary); } + + protected virtual HtmlString GenerateLabel([NotNull] ModelMetadata metadata, + string htmlFieldName, + string labelText, + object htmlAttributes) + { + // TODO: This needs to be updated after ModelMetadata has a DisplayName property + string resolvedLabelText = labelText ?? metadata.PropertyName; + if (resolvedLabelText == null) + { + resolvedLabelText = string.IsNullOrEmpty(htmlFieldName) ? + string.Empty : + htmlFieldName.Split('.').Last(); + } + + if (string.IsNullOrEmpty(resolvedLabelText)) + { + return HtmlString.Empty; + } + + TagBuilder tag = new TagBuilder("label"); + tag.Attributes.Add( + "for", + TagBuilder.CreateSanitizedId( + ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName), + IdAttributeDotReplacement)); + tag.SetInnerText(resolvedLabelText); + tag.MergeAttributes(AnonymousObjectToHtmlAttributes(htmlAttributes), replaceExisting: true); + return tag.ToHtmlString(TagRenderMode.Normal); + } protected virtual HtmlString GenerateLink( [NotNull] string linkText, diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs index c2af9e14c2..3308d35b0b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs @@ -82,6 +82,31 @@ namespace Microsoft.AspNet.Mvc.Rendering additionalViewData); } + /// + public HtmlString DisplayNameFor([NotNull] Expression> expression) + { + var metadata = GetModelMetadata(expression); + return GenerateDisplayName(metadata, ExpressionHelper.GetExpressionText(expression)); + } + + /// + public HtmlString DisplayNameForInnerType(Expression> expression) + { + var metadata = ExpressionMetadataProvider. + FromLambdaExpression( + expression, + new ViewDataDictionary( + MetadataProvider), + MetadataProvider); + var expressionText = ExpressionHelper.GetExpressionText(expression); + if (metadata == null) + { + throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionText)); + } + + return GenerateDisplayName(metadata, expressionText); + } + /// public HtmlString HiddenFor([NotNull] Expression> expression, object htmlAttributes) @@ -91,6 +116,13 @@ namespace Microsoft.AspNet.Mvc.Rendering htmlAttributes: htmlAttributes); } + /// + public HtmlString LabelFor([NotNull] Expression> expression, string labelText, object htmlAttributes) + { + var metadata = GetModelMetadata(expression); + return GenerateLabel(metadata, ExpressionHelper.GetExpressionText(expression), labelText, htmlAttributes); + } + /// public HtmlString NameFor([NotNull] Expression> expression) { diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperDisplayNameExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperDisplayNameExtensions.cs new file mode 100644 index 0000000000..e4ed3cf7d7 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperDisplayNameExtensions.cs @@ -0,0 +1,36 @@ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +namespace Microsoft.AspNet.Mvc.Rendering +{ + /// + /// DisplayName-related extensions for and . + /// + public static class HtmlHelperDisplayNameExtensions + { + /// + /// Gets the display name for the current model. + /// + /// The instance that this method extends. + /// An that represents HTML markup. + public static HtmlString DisplayNameForModel([NotNull] this IHtmlHelper htmlHelper) + { + return htmlHelper.DisplayName(string.Empty); + } + + /// + /// Gets the display name for the model. + /// + /// The instance that this method extends. + /// An expression that identifies the object that contains the display name. + /// + /// The display name for the model. + /// + public static HtmlString DisplayNameFor(this IHtmlHelper> htmlHelper, + Expression> expression) + { + return htmlHelper.DisplayNameForInnerType(expression); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperLabelExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperLabelExtensions.cs new file mode 100644 index 0000000000..ed16dab542 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperLabelExtensions.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq.Expressions; + +namespace Microsoft.AspNet.Mvc.Rendering +{ + public static class HtmlHelperLabelExtensions + { + public static HtmlString Label([NotNull] this IHtmlHelper html, string expression) + { + return html.Label(expression, + labelText: null, + htmlAttributes: null); + } + + public static HtmlString Label([NotNull] this IHtmlHelper html, + string expression, + string labelText) + { + return html.Label(expression, labelText, htmlAttributes: null); + } + + public static HtmlString LabelFor([NotNull] this IHtmlHelper html, + [NotNull] Expression> expression) + { + return html.LabelFor(expression, labelText: null, htmlAttributes: null); + } + + public static HtmlString LabelFor([NotNull] this IHtmlHelper html, + [NotNull] Expression> expression, + string labelText) + { + return html.LabelFor(expression, labelText, htmlAttributes: null); + } + + public static HtmlString LabelFor([NotNull] this IHtmlHelper html, + [NotNull] Expression> expression, + object htmlAttributes) + { + return html.LabelFor(expression, labelText: null, htmlAttributes: htmlAttributes); + } + + public static HtmlString LabelForModel([NotNull] this IHtmlHelper html) + { + return LabelForModel(html, labelText: null); + } + + public static HtmlString LabelForModel([NotNull] this IHtmlHelper html, string labelText) + { + return html.Label(expression: string.Empty, labelText: labelText, htmlAttributes: null); + } + + public static HtmlString LabelForModel([NotNull] this IHtmlHelper html, + object htmlAttributes) + { + return html.Label(expression: string.Empty, labelText: null, htmlAttributes: htmlAttributes); + } + + public static HtmlString LabelForModel([NotNull] this IHtmlHelper html, + string labelText, + object htmlAttributes) + { + return html.Label(expression: string.Empty, labelText: labelText, htmlAttributes: htmlAttributes); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs index 07d11c07af..976989bdcc 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs @@ -174,6 +174,34 @@ namespace Microsoft.AspNet.Mvc.Rendering /// The HTML markup for each property in the model. HtmlString DisplayForModel(string templateName, string htmlFieldName, object additionalViewData); + /// + /// Gets the display name. + /// + /// An expression that identifies the object that contains the display name. + /// + /// The display name. + /// + HtmlString DisplayName(string expression); + + /// + /// Gets the display name for the model. + /// + /// An expression that identifies the object that contains the display name. + /// The type of the value. + /// + /// The display name for the model. + /// + HtmlString DisplayNameFor(Expression> expression); + + /// + /// Gets the display name for the inner model if the current model represents a collection. + /// + /// The type of the inner model + /// The type of the value. + /// An expression that identifies the object that contains the display name. + /// The display name for the inner model. + HtmlString DisplayNameForInnerType(Expression> expression); + /// /// Returns a single-selection HTML {select} element using the specified name of the form field, /// list items, option label, and HTML attributes. @@ -269,6 +297,30 @@ namespace Microsoft.AspNet.Mvc.Rendering HtmlString HiddenFor([NotNull] Expression> expression, object htmlAttributes); + /// + /// Returns an HTML label element and the property name of the property that is represented by the specified expression. + /// + /// An expression that identifies the property to display. + /// The label text. + /// An object that contains the HTML attributes to set for the element. + /// + /// An HTML label element and the property name of the property that is represented by the expression. + /// + HtmlString Label(string expression, string labelText, object htmlAttributes); + + /// + /// Returns an HTML label element and the property name of the property that is represented by the specified expression. + /// + /// An expression that identifies the property to display. + /// An object that contains the HTML attributes to set for the element. + /// The type of the value. + /// + /// An HTML label element and the property name of the property that is represented by the expression. + /// + HtmlString LabelFor(Expression> expression, + string labelText, + object htmlAttributes); + /// /// Gets the full HTML field name for the given expression . ///