diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs
index 22f12864d4..9feec71ef2 100644
--- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs
@@ -662,6 +662,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
}
tagBuilder.MergeAttribute("name", fullName, true);
+
+ AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
// If there are any errors for a named field, we add this CSS attribute.
@@ -1170,12 +1172,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
var suppliedTypeString = tagBuilder.Attributes["type"];
if (_placeholderInputTypes.Contains(suppliedTypeString))
{
- modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider);
- var placeholder = modelExplorer.Metadata.Placeholder;
- if (!string.IsNullOrEmpty(placeholder))
- {
- tagBuilder.MergeAttribute("placeholder", placeholder);
- }
+ AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
}
var valueParameter = FormatValue(value, format);
@@ -1288,6 +1285,27 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
return tagBuilder;
}
+ ///
+ /// Adds a placeholder attribute to the .
+ ///
+ /// A instance for the current scope.
+ /// A instance.
+ /// The for the .
+ /// Expression name, relative to the current model.
+ protected virtual void AddPlaceholderAttribute(
+ ViewDataDictionary viewData,
+ TagBuilder tagBuilder,
+ ModelExplorer modelExplorer,
+ string expression)
+ {
+ modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(expression, viewData, _metadataProvider);
+ var placeholder = modelExplorer.Metadata.Placeholder;
+ if (!string.IsNullOrEmpty(placeholder))
+ {
+ tagBuilder.MergeAttribute("placeholder", placeholder);
+ }
+ }
+
///
/// Adds validation attributes to the if client validation
/// is enabled.
diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextAreaTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextAreaTest.cs
new file mode 100644
index 0000000000..e9014ad9a8
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextAreaTest.cs
@@ -0,0 +1,54 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Mvc.TestCommon;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.Rendering
+{
+ public class HtmlHelperTextBoxAreaTest
+ {
+ [Fact]
+ public void TextAreaFor_GeneratesPlaceholderAttribute_WhenDisplayAttributePromptIsSetAndTypeIsValid()
+ {
+ // Arrange
+ var model = new TextAreaModelWithAPlaceholder();
+ var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
+
+ // Act
+ var textArea = helper.TextAreaFor(m => m.Property1);
+
+ // Assert
+ var result = HtmlContentUtilities.HtmlContentToString(textArea);
+ Assert.Contains(@"placeholder=""HtmlEncode[[placeholder]]""", result, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void TextAreaFor_DoesNotGeneratePlaceholderAttribute_WhenNoPlaceholderPresentInModel()
+ {
+ // Arrange
+ var model = new TextAreaModelWithoutAPlaceholder();
+ var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
+
+ // Act
+ var textArea = helper.TextAreaFor(m => m.Property1);
+
+ // Assert
+ var result = HtmlContentUtilities.HtmlContentToString(textArea);
+ Assert.DoesNotContain(@"placeholder=""HtmlEncode[[placeholder]]""", result, StringComparison.Ordinal);
+ }
+
+ private class TextAreaModelWithAPlaceholder
+ {
+ [Display(Prompt = "placeholder")]
+ public string Property1 { get; set; }
+ }
+
+ private class TextAreaModelWithoutAPlaceholder
+ {
+ public string Property1 { get; set; }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextBoxTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextBoxTest.cs
index d18b6e5367..d2568dcbc1 100644
--- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextBoxTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Rendering/HtmlHelperTextBoxTest.cs
@@ -1,8 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using Microsoft.AspNetCore.Mvc.TestCommon;
+using System;
using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Mvc.TestCommon;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Rendering
@@ -23,10 +24,11 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
// Act
- var result = HtmlContentUtilities.HtmlContentToString(helper.TextBoxFor(m => m.Property1, new { type }));
+ var textBox = helper.TextBoxFor(m => m.Property1, new { type });
- // Assert
- Assert.True(result.Contains(@"placeholder=""HtmlEncode[[placeholder]]"""));
+ // Assert
+ var result = HtmlContentUtilities.HtmlContentToString(textBox);
+ Assert.Contains(@"placeholder=""HtmlEncode[[placeholder]]""", result, StringComparison.Ordinal);
}
[Theory]
@@ -49,10 +51,11 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
// Act
- var result = HtmlContentUtilities.HtmlContentToString(helper.TextBoxFor(m => m.Property1, new { type }));
+ var textBox = helper.TextBoxFor(m => m.Property1, new { type });
- // Assert
- Assert.False(result.Contains(@"placeholder=""HtmlEncode[[placeholder]]"""));
+ // Assert
+ var result = HtmlContentUtilities.HtmlContentToString(textBox);
+ Assert.DoesNotContain(@"placeholder=""HtmlEncode[[placeholder]]""", result, StringComparison.Ordinal);
}
private class TextBoxModel