Add rendering logic for PreElement and PostElement on TagHelperOutput.

- Added unit tests to validate that the properties were rendered correctly.
- Modified functional tests to utilize PreElement and PostElement.

aspnet/Razor#341
This commit is contained in:
N. Taylor Mullen 2015-04-13 14:52:06 -07:00
parent d0e5118741
commit 2c4c35e126
5 changed files with 362 additions and 44 deletions

View File

@ -278,6 +278,8 @@ namespace Microsoft.AspNet.Mvc.Razor
var tagHelperOutput = tagHelperExecutionContext.Output; var tagHelperOutput = tagHelperExecutionContext.Output;
var isTagNameNullOrWhitespace = string.IsNullOrWhiteSpace(tagHelperOutput.TagName); var isTagNameNullOrWhitespace = string.IsNullOrWhiteSpace(tagHelperOutput.TagName);
WriteTagHelperContentTo(writer, tagHelperOutput.PreElement);
if (!isTagNameNullOrWhitespace) if (!isTagNameNullOrWhitespace)
{ {
writer.Write('<'); writer.Write('<');
@ -324,6 +326,8 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
writer.Write(string.Format(CultureInfo.InvariantCulture, "</{0}>", tagHelperOutput.TagName)); writer.Write(string.Format(CultureInfo.InvariantCulture, "</{0}>", tagHelperOutput.TagName));
} }
WriteTagHelperContentTo(writer, tagHelperOutput.PostElement);
} }
private void WriteTagHelperContentTo(TextWriter writer, TagHelperContent content) private void WriteTagHelperContentTo(TextWriter writer, TagHelperContent content)

View File

@ -28,12 +28,12 @@
<div> <div>
<p>This website has <strong style="font-size: 1.25em;text-decoration: underline;">not</strong> been approved yet. Visit <strong><a target="_blank" href="http://www.contoso.com">www.contoso.com</a></strong> for <strong>more</strong> information.</p> <p>This website has <em><strong style="font-size: 1.25em;text-decoration: underline;">not</strong></em> been approved yet. Visit <strong><a target="_blank" href="http://www.contoso.com">www.contoso.com</a></strong> for <strong>more</strong> information.</p>
</div> </div>
<div> <div>
<h3 style="font-family: cursive;">Current Tag Cloud from Tag Helper</h3> <h3 style="font-family: cursive;">Current Tag Cloud from Tag Helper</h3>
<section>["Lorem","ipsum","dolor","sit","amet","consectetur","adipisicing","elit","sed","do","eiusmod","tempor","incididunt","ut","labore","et","dolore","magna","aliquaUt","enim"]</section> <div>["Lorem","ipsum","dolor","sit","amet","consectetur","adipisicing","elit","sed","do","eiusmod","tempor","incididunt","ut","labore","et","dolore","magna","aliquaUt","enim"]</div>
<h3 style="font-family: cursive;">Current Tag Cloud from ViewComponentHelper:</h3> <h3 style="font-family: cursive;">Current Tag Cloud from ViewComponentHelper:</h3>
<section>["Lorem","ipsum","dolor","sit","amet","consectetur","adipisicing","elit","sed","do","eiusmod","tempor","incididunt","ut","labore"]</section> <section>["Lorem","ipsum","dolor","sit","amet","consectetur","adipisicing","elit","sed","do","eiusmod","tempor","incididunt","ut","labore"]</section>
</div> </div>

View File

@ -722,85 +722,365 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
{ {
// parameters: TagName, Attributes, SelfClosing, PreContent, Content, PostContent // parameters: TagName, Attributes, SelfClosing, PreContent, Content, PostContent
GetTagHelperOutput("div", new Dictionary<string, object>(), false, null, "Hello World!", null), GetTagHelperOutput(
tagName: "div",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<div>Hello World!</div>" "<div>Hello World!</div>"
}, },
{ {
GetTagHelperOutput(null, new Dictionary<string, object>(), false, null, "Hello World!", null), GetTagHelperOutput(
"Hello World!" tagName: null,
}, attributes: new Dictionary<string, object>(),
{ selfClosing: false,
GetTagHelperOutput(" ", new Dictionary<string, object>(), false, null, "Hello World!", null), preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"Hello World!" "Hello World!"
}, },
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", tagName: " ",
new Dictionary<string, object>() { { "test", "testVal" } }, attributes: new Dictionary<string, object>(),
false, selfClosing: false,
null, preElement: null,
"Hello World!", preContent: null,
null), content: "Hello World!",
postContent: null,
postElement: null),
"Hello World!"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new Dictionary<string, object>() { { "test", "testVal" } },
selfClosing: false,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"testVal\">Hello World!</p>" "<p test=\"testVal\">Hello World!</p>"
}, },
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", tagName: "p",
new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } }, attributes: new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } },
false, selfClosing: false,
null, preElement: null,
"Hello World!", preContent: null,
null), content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"testVal\" something=\" spaced \">Hello World!</p>" "<p test=\"testVal\" something=\" spaced \">Hello World!</p>"
}, },
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", tagName: "p",
new Dictionary<string, object>() { { "test", "testVal" } }, attributes: new Dictionary<string, object>() { { "test", "testVal" } },
true, selfClosing: true,
null, preElement: null,
"Hello World!", preContent: null,
null), content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"testVal\" />" "<p test=\"testVal\" />"
}, },
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", tagName: "p",
new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } }, attributes: new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } },
true, selfClosing: true,
null, preElement: null,
"Hello World!", preContent: null,
null), content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"testVal\" something=\" spaced \" />" "<p test=\"testVal\" something=\" spaced \" />"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, object>(), false, "Hello World!", null, null), GetTagHelperOutput(
tagName: "p",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: "Hello World!",
content: null,
postContent: null,
postElement: null),
"<p>Hello World!</p>" "<p>Hello World!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, object>(), false, null, "Hello World!", null), GetTagHelperOutput(
tagName: "p",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p>Hello World!</p>" "<p>Hello World!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, object>(), false, null, null, "Hello World!"), GetTagHelperOutput(
tagName: "p",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: null,
content: null,
postContent: "Hello World!",
postElement: null),
"<p>Hello World!</p>" "<p>Hello World!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, object>(), false, "Hello", "Test", "World!"), GetTagHelperOutput(
tagName: "p",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p>HelloTestWorld!</p>" "<p>HelloTestWorld!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, object>(), true, "Hello", "Test", "World!"), GetTagHelperOutput(
tagName: "p",
attributes: new Dictionary<string, object>(),
selfClosing: true,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p />" "<p />"
}, },
{ {
GetTagHelperOutput("custom", new Dictionary<string, object>(), false, "Hello", "Test", "World!"), GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<custom>HelloTestWorld!</custom>" "<custom>HelloTestWorld!</custom>"
}, },
{ {
GetTagHelperOutput("random", new Dictionary<string, object>(), true, "Hello", "Test", "World!"), GetTagHelperOutput(
tagName: "random",
attributes: new Dictionary<string, object>(),
selfClosing: true,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<random />" "<random />"
} },
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom></custom>"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object> { { "test", "testVal" } },
selfClosing: true,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object> { { "test", "testVal" } },
selfClosing: true,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom test=\"testVal\" />"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: true,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom />"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom></custom>After"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object> { { "test", "testVal" } },
selfClosing: true,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object> { { "test", "testVal" } },
selfClosing: true,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom test=\"testVal\" />After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: true,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom />After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom>HelloTestWorld!</custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object> { { "test", "testVal" } },
selfClosing: false,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom test=\"testVal\">HelloTestWorld!</custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new Dictionary<string, object>(),
selfClosing: true,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom />After"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object>(),
selfClosing: true,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object>(),
selfClosing: false,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: null,
attributes: new Dictionary<string, object> { { "test", "testVal" } },
selfClosing: false,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
}; };
} }
} }
@ -937,18 +1217,22 @@ namespace Microsoft.AspNet.Mvc.Razor
string tagName, string tagName,
IDictionary<string, object> attributes, IDictionary<string, object> attributes,
bool selfClosing, bool selfClosing,
string preElement,
string preContent, string preContent,
string content, string content,
string postContent) string postContent,
string postElement)
{ {
var output = new TagHelperOutput(tagName, attributes) var output = new TagHelperOutput(tagName, attributes)
{ {
SelfClosing = selfClosing SelfClosing = selfClosing
}; };
output.PreElement.SetContent(preElement);
output.PreContent.SetContent(preContent); output.PreContent.SetContent(preContent);
output.Content.SetContent(content); output.Content.SetContent(content);
output.PostContent.SetContent(postContent); output.PostContent.SetContent(postContent);
output.PostElement.SetContent(postElement);
return output; return output;
} }

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace TagHelpersWebSite.TagHelpers
{
[TargetElement(Attributes = nameof(Surround))]
public class SurroundTagHelper : TagHelper
{
public override int Order
{
get
{
// Run first
return int.MinValue;
}
}
public string Surround { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var surroundingTagName = Surround.ToLowerInvariant();
output.PreElement.SetContent($"<{surroundingTagName}>");
output.PostElement.SetContent($"</{surroundingTagName}>");
}
}
}

View File

@ -17,16 +17,16 @@
} }
<div condition="!Model.Approved"> <div condition="!Model.Approved">
<p>This website has <strong>not</strong> been approved yet. Visit www.contoso.com for <strong make-pretty="false">more</strong> information.</p> <p>This website has <strong surround="em">not</strong> been approved yet. Visit www.contoso.com for <strong make-pretty="false">more</strong> information.</p>
</div> </div>
<div> <div>
<h3>Current Tag Cloud from Tag Helper</h3> <h3>Current Tag Cloud from Tag Helper</h3>
<section><tag-cloud count="Model.TagsToShow" /></section> <tag-cloud count="Model.TagsToShow" surround="div" />
<h3>Current Tag Cloud from ViewComponentHelper:</h3> <h3>Current Tag Cloud from ViewComponentHelper:</h3>
<section>@await Component.InvokeAsync("Tags", 15)</section> <section>@await Component.InvokeAsync("Tags", 15)</section>
</div> </div>
@section footerContent { @section footerContent {
<p condition="Model.Approved">&copy; @Model.CopyrightYear - My ASP.NET Application</p> <p condition="Model.Approved" surround="section">&copy; @Model.CopyrightYear - My ASP.NET Application</p>
} }