Fix #3087 - use IHtmlContentBuilder in TagBuilder

Improves authoring experience when using TagBuilder by taking advantage of
IHtmlContentBuilder an its extension methods.

TagBuilder.InnerHtml is no longer settable.
This commit is contained in:
Ryan Nowak 2015-09-16 10:03:03 -07:00
parent d03a851ab3
commit a707311d9e
8 changed files with 74 additions and 96 deletions

View File

@ -52,13 +52,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
selectTag.AddCssClass("tri-state");
selectTag.Attributes["disabled"] = "disabled";
var content = new BufferedHtmlContent();
foreach (var item in TriStateValues(value))
{
content.Append(DefaultHtmlGenerator.GenerateOption(item, item.Text));
selectTag.InnerHtml.Append(DefaultHtmlGenerator.GenerateOption(item, item.Text));
}
selectTag.InnerHtml = content;
return selectTag;
}
@ -251,14 +249,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
if (!string.IsNullOrEmpty(label))
{
var labelTag = new TagBuilder("div");
labelTag.SetInnerText(label);
labelTag.InnerHtml.SetContent(label);
labelTag.AddCssClass("display-label");
content.AppendLine(labelTag);
}
var valueDivTag = new TagBuilder("div");
valueDivTag.AddCssClass("display-field");
valueDivTag.InnerHtml = templateBuilderResult;
valueDivTag.InnerHtml.SetContent(templateBuilderResult);
content.AppendLine(valueDivTag);
}
else
@ -304,7 +302,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var hyperlinkTag = new TagBuilder("a");
hyperlinkTag.MergeAttribute("href", uriString);
hyperlinkTag.SetInnerText(linkedText);
hyperlinkTag.InnerHtml.SetContent(linkedText);
return hyperlinkTag;
}
}

View File

@ -274,22 +274,20 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var labelTag = new TagBuilder("div");
labelTag.AddCssClass("editor-label");
labelTag.InnerHtml = label;
labelTag.InnerHtml.SetContent(label);
content.AppendLine(labelTag);
}
var valueDivTag = new TagBuilder("div");
valueDivTag.AddCssClass("editor-field");
var innerContent = new BufferedHtmlContent();
innerContent.Append(templateBuilderResult);
innerContent.AppendEncoded(" ");
innerContent.Append(htmlHelper.ValidationMessage(
valueDivTag.InnerHtml.Append(templateBuilderResult);
valueDivTag.InnerHtml.AppendEncoded(" ");
valueDivTag.InnerHtml.Append(htmlHelper.ValidationMessage(
propertyMetadata.PropertyName,
message: null,
htmlAttributes: null,
tag: null));
valueDivTag.InnerHtml = innerContent;
content.AppendLine(valueDivTag);
}

View File

@ -271,7 +271,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var idString =
TagBuilder.CreateSanitizedId(GetFullHtmlFieldName(viewContext, expression), IdAttributeDotReplacement);
tagBuilder.Attributes.Add("for", idString);
tagBuilder.SetInnerText(resolvedLabelText);
tagBuilder.InnerHtml.SetContent(resolvedLabelText);
tagBuilder.MergeAttributes(GetHtmlAttributeDictionaryOrNull(htmlAttributes), replaceExisting: true);
return tagBuilder;
@ -440,10 +440,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
// Convert each ListItem to an <option> tag and wrap them with <optgroup> if requested.
var listItemBuilder = GenerateGroupsAndOptions(optionLabel, selectList);
var tagBuilder = new TagBuilder("select")
{
InnerHtml = listItemBuilder
};
var tagBuilder = new TagBuilder("select");
tagBuilder.InnerHtml.SetContent(listItemBuilder);
tagBuilder.MergeAttributes(GetHtmlAttributeDictionaryOrNull(htmlAttributes));
tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
tagBuilder.GenerateId(fullName, IdAttributeDotReplacement);
@ -537,11 +535,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
// The first newline is always trimmed when a TextArea is rendered, so we add an extra one
// in case the value being rendered is something like "\r\nHello".
var innerContent = new BufferedHtmlContent();
innerContent.Append(HtmlString.NewLine);
innerContent.Append(value);
tagBuilder.InnerHtml = innerContent;
// in case the value being rendered is something like "\r\nHello"
tagBuilder.InnerHtml.AppendLine();
tagBuilder.InnerHtml.Append(value);
return tagBuilder;
}
@ -630,11 +626,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
if (!string.IsNullOrEmpty(message))
{
tagBuilder.SetInnerText(message);
tagBuilder.InnerHtml.SetContent(message);
}
else if (modelError != null)
{
tagBuilder.SetInnerText(ValidationHelpers.GetUserErrorMessageOrDefault(modelError, modelState));
tagBuilder.InnerHtml.SetContent(
ValidationHelpers.GetUserErrorMessageOrDefault(modelError, modelState));
}
if (formContext != null)
@ -672,7 +669,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
headerTag = viewContext.ValidationSummaryMessageElement;
}
var messageTag = new TagBuilder(headerTag);
messageTag.SetInnerText(message);
messageTag.InnerHtml.SetContent(message);
wrappedMessage.AppendLine(messageTag);
}
else
@ -682,10 +679,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
// If excludePropertyErrors is true, describe any validation issue with the current model in a single item.
// Otherwise, list individual property errors.
var htmlSummary = new BufferedHtmlContent();
var isHtmlSummaryModified = false;
var modelStates = ValidationHelpers.GetModelStateList(viewContext.ViewData, excludePropertyErrors);
var htmlSummary = new TagBuilder("ul");
foreach (var modelState in modelStates)
{
foreach (var modelError in modelState.Errors)
@ -695,8 +692,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
if (!string.IsNullOrEmpty(errorText))
{
var listItem = new TagBuilder("li");
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem);
listItem.InnerHtml.SetContent(errorText);
htmlSummary.InnerHtml.AppendLine(listItem);
isHtmlSummaryModified = true;
}
}
@ -704,15 +701,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
if (!isHtmlSummaryModified)
{
htmlSummary.AppendEncoded(HiddenListItem);
htmlSummary.Append(HtmlString.NewLine);
htmlSummary.InnerHtml.AppendEncoded(HiddenListItem);
htmlSummary.InnerHtml.AppendLine();
}
var unorderedList = new TagBuilder("ul")
{
InnerHtml = htmlSummary
};
var tagBuilder = new TagBuilder("div");
tagBuilder.MergeAttributes(GetHtmlAttributeDictionaryOrNull(htmlAttributes));
@ -724,11 +716,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
tagBuilder.AddCssClass(HtmlHelper.ValidationSummaryCssClassName);
}
var innerContent = new BufferedHtmlContent();
innerContent.Append(wrappedMessage);
innerContent.Append(unorderedList);
tagBuilder.InnerHtml = innerContent;
tagBuilder.InnerHtml.Append(wrappedMessage);
tagBuilder.InnerHtml.Append(htmlSummary);
if (formContext != null && !excludePropertyErrors)
{
@ -911,7 +901,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
internal static TagBuilder GenerateOption(SelectListItem item, string text)
{
var tagBuilder = new TagBuilder("option");
tagBuilder.SetInnerText(text);
tagBuilder.InnerHtml.SetContent(text);
if (item.Value != null)
{
@ -1117,10 +1107,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] string url,
object htmlAttributes)
{
var tagBuilder = new TagBuilder("a")
{
InnerHtml = new StringHtmlContent(linkText),
};
var tagBuilder = new TagBuilder("a");
tagBuilder.InnerHtml.SetContent(linkText);
tagBuilder.MergeAttributes(GetHtmlAttributeDictionaryOrNull(htmlAttributes));
tagBuilder.MergeAttribute("href", url);
@ -1317,13 +1305,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
groupBuilder.MergeAttribute("disabled", "disabled");
}
var optGroupContent = new BufferedHtmlContent().Append(HtmlString.NewLine);
groupBuilder.InnerHtml.AppendLine();
foreach (var item in group)
{
optGroupContent.AppendLine(GenerateOption(item));
groupBuilder.InnerHtml.AppendLine(GenerateOption(item));
}
groupBuilder.InnerHtml = optGroupContent;
listItemBuilder.AppendLine(groupBuilder);
}
else

View File

@ -24,13 +24,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagName = tagName;
Attributes = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
InnerHtml = new BufferedHtmlContent();
}
public IDictionary<string, string> Attributes { get; private set; }
/// <summary>
/// Gets the set of attributes that will be written to the tag.
/// </summary>
public IDictionary<string, string> Attributes { get; }
public IHtmlContent InnerHtml { get; [param: NotNull] set; } = HtmlString.Empty;
public IHtmlContentBuilder InnerHtml { get; }
public string TagName { get; private set; }
public string TagName { get; }
/// <summary>
/// The <see cref="Rendering.TagRenderMode"/> with which the tag is written.
@ -187,11 +192,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
}
public void SetInnerText(string innerText)
{
InnerHtml = new StringHtmlContent(innerText);
}
/// <inheritdoc />
public void WriteTo(TextWriter writer, IHtmlEncoder encoder)
{

View File

@ -5,13 +5,13 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Actions;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.WebEncoders.Testing;
using Moq;
using Xunit;
@ -143,10 +143,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string childContent, string outputContent, string expectedOutputContent)
{
// Arrange
var tagBuilder = new TagBuilder("span2")
{
InnerHtml = new HtmlString("New HTML")
};
var tagBuilder = new TagBuilder("span2");
tagBuilder.InnerHtml.SetContentEncoded("New HTML");
tagBuilder.Attributes.Add("data-foo", "bar");
tagBuilder.Attributes.Add("data-hello", "world");
@ -204,10 +202,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string childContent, string expectedOutputContent)
{
// Arrange
var tagBuilder = new TagBuilder("span2")
{
InnerHtml = new HtmlString("New HTML")
};
var tagBuilder = new TagBuilder("span2");
tagBuilder.InnerHtml.SetContentEncoded("New HTML");
tagBuilder.Attributes.Add("data-foo", "bar");
tagBuilder.Attributes.Add("data-hello", "world");

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Actions;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -13,7 +14,6 @@ using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Microsoft.Framework.WebEncoders;
using Moq;
using Xunit;
@ -131,11 +131,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public async Task ProcessAsync_MergesTagBuilderFromGenerateValidationSummary()
{
// Arrange
var tagBuilder = new TagBuilder("span2")
{
InnerHtml = new HtmlString("New HTML")
};
var tagBuilder = new TagBuilder("span2");
tagBuilder.InnerHtml.SetContentEncoded("New HTML");
tagBuilder.Attributes.Add("data-foo", "bar");
tagBuilder.Attributes.Add("data-hello", "world");
tagBuilder.Attributes.Add("anything", "something");
@ -225,10 +222,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public async Task ProcessAsync_GeneratesValidationSummaryWhenNotNone(ValidationSummary validationSummary)
{
// Arrange
var tagBuilder = new TagBuilder("span2")
{
InnerHtml = new HtmlString("New HTML")
};
var tagBuilder = new TagBuilder("span2");
tagBuilder.InnerHtml.SetContentEncoded("New HTML");
var generator = new Mock<IHtmlGenerator>();
generator

View File

@ -3,8 +3,8 @@
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.Framework.WebEncoders.Testing;
using Xunit;
@ -92,21 +92,6 @@ namespace Microsoft.AspNet.Mvc.Core.Rendering
}
}
[Fact]
public void SetInnerText_HtmlEncodesValue()
{
// Arrange
var tagBuilder = new TagBuilder("p");
// Act
tagBuilder.SetInnerText("TestValue");
// Assert
Assert.Equal(
"HtmlEncode[[TestValue]]",
HtmlContentUtilities.HtmlContentToString(tagBuilder.InnerHtml));
}
[Theory]
[InlineData("HelloWorld", "HelloWorld")]
[InlineData("¡HelloWorld", "zHelloWorld")]
@ -119,5 +104,23 @@ namespace Microsoft.AspNet.Mvc.Core.Rendering
// Assert
Assert.Equal(output, result);
}
[Fact]
public void WriteTo_IncludesInnerHtml()
{
// Arrange
var tagBuilder = new TagBuilder("p");
tagBuilder.InnerHtml.AppendEncoded("<span>Hello</span>");
tagBuilder.InnerHtml.Append(", World!");
// Act
using (var writer = new StringWriter())
{
tagBuilder.WriteTo(writer, new CommonTestEncoder());
// Assert
Assert.Equal("<p><span>Hello</span>HtmlEncode[[, World!]]</p>", writer.ToString());
}
}
}
}

View File

@ -1,6 +1,7 @@
// 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.AspNet.Html.Abstractions;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
@ -26,8 +27,8 @@ namespace ActivatorWebSite.TagHelpers
(HtmlHelper as ICanHasViewContext)?.Contextualize(ViewContext);
var builder = new TagBuilder("h2");
var title = ViewContext.ViewBag.Title;
builder.SetInnerText(title);
var title = (string)ViewContext.ViewBag.Title;
builder.InnerHtml.SetContent(title);
output.PreContent.SetContent(builder);
}
}