Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit d3af121

Browse files
author
NTaylorMullen
committed
Addressed code review comments.
1 parent fd26c23 commit d3af121

File tree

2 files changed

+98
-52
lines changed

2 files changed

+98
-52
lines changed

src/Microsoft.AspNet.Mvc.TagHelpers/TagHelperOutputExtensions.cs

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,62 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
1414
/// </summary>
1515
public static class TagHelperOutputExtensions
1616
{
17+
/// <summary>
18+
/// Copies a user provided attribute from <paramref name="context"/>'s
19+
/// <see cref="TagHelperContext.AllAttributes"/> to <paramref name="tagHelperOutput"/>'s
20+
/// <see cref="TagHelperOutput.Attributes"/>.
21+
/// </summary>
22+
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
23+
/// <param name="attributeName">The name of the bound attribute.</param>
24+
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
25+
/// <remarks>Only copies the attribute if <paramref name="tagHelperOutput"/>'s
26+
/// <see cref="TagHelperOutput.Attributes"/> does not contain an attribute with the given
27+
/// <paramref name="attributeName"/></remarks>
28+
public static void CopyHtmlAttribute(this TagHelperOutput tagHelperOutput,
29+
string attributeName,
30+
TagHelperContext context)
31+
{
32+
// We look for the original attribute so we can restore the exact attribute name the user typed.
33+
var entry = context.AllAttributes.Single(attribute =>
34+
attribute.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
35+
var originalAttribute = new KeyValuePair<string, string>(entry.Key, entry.Value.ToString());
36+
37+
if (!tagHelperOutput.Attributes.ContainsKey(originalAttribute.Key))
38+
{
39+
tagHelperOutput.Attributes.Add(originalAttribute);
40+
}
41+
}
42+
43+
/// <summary>
44+
/// Returns all attributes from <paramref name="tagHelperOutput"/>'s
45+
/// <see cref="TagHelperOutput.Attributes"/> that have the given <paramref name="prefix"/>.
46+
/// </summary>
47+
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
48+
/// <param name="prefix">A prefix to look for.</param>
49+
/// <returns><see cref="KeyValuePair{string, string}"/>s with <see cref="KeyValuePair{string, string}.Key"/>
50+
/// starting with the given <paramref name="prefix"/>.</returns>
51+
public static IEnumerable<KeyValuePair<string, string>> FindPrefixedAttributes(
52+
this TagHelperOutput tagHelperOutput, string prefix)
53+
{
54+
// TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed.
55+
56+
// We're only interested in HTML attributes that have the desired prefix.
57+
var prefixedAttributes = tagHelperOutput.Attributes
58+
.Where(attribute => attribute.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
59+
.ToArray();
60+
61+
return prefixedAttributes;
62+
}
63+
1764
/// <summary>
1865
/// Merges the given <paramref name="tagBuilder"/> into the <paramref name="tagHelperOutput"/>.
1966
/// </summary>
20-
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/>.</param>
67+
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
2168
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge.</param>
69+
/// <remarks><paramref name="tagHelperOutput"/>'s <see cref="TagHelperOutput.Content"/> has the given
70+
/// <paramref name="tagBuilder"/>s <see cref="TagBuilder.InnerHtml"/> appended to it. This is to ensure
71+
/// multiple <see cref="ITagHelper"/>s running on the same HTML tag don't overwrite each other; therefore,
72+
/// this method may not be appropriate for all <see cref="ITagHelper"/> scenarios.</remarks>
2273
public static void Merge(this TagHelperOutput tagHelperOutput, TagBuilder tagBuilder)
2374
{
2475
tagHelperOutput.TagName = tagBuilder.TagName;
@@ -31,8 +82,10 @@ public static void Merge(this TagHelperOutput tagHelperOutput, TagBuilder tagBui
3182
/// Merges the given <see cref="tagBuilder"/>'s <see cref="TagBuilder.Attributes"/> into the
3283
/// <paramref name="tagHelperOutput"/>.
3384
/// </summary>
34-
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/>.</param>
85+
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
3586
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge attributes from.</param>
87+
/// <remarks>Existing <see cref="TagHelperOutput.Attributes"/> on the given <paramref name="tagHelperOutput"/>
88+
/// are not overriden; "class" attributes are merged with spaces.</remarks>
3689
public static void MergeAttributes(this TagHelperOutput tagHelperOutput, TagBuilder tagBuilder)
3790
{
3891
foreach (var attribute in tagBuilder.Attributes)
@@ -49,49 +102,18 @@ public static void MergeAttributes(this TagHelperOutput tagHelperOutput, TagBuil
49102
}
50103

51104
/// <summary>
52-
/// Returns and removes all attributes from <paramref name="tagHelperOutput"/>'s
53-
/// <see cref="TagHelperOutput.Attributes"/> that have the given <paramref name="prefix"/>.
105+
/// Removes the given <paramref name="attributes"/> from <paramref name="tagHelperOutput"/>'s
106+
/// <see cref="TagHelperOutput.Attributes"/>.
54107
/// </summary>
55-
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/>.</param>
56-
/// <param name="prefix">The prefix to </param>
57-
/// <returns><see cref="KeyValuePair{string, string}"/>s whos <see cref="KeyValuePair{string, string}.Key"/>
58-
/// starts with the given <paramref name="prefix"/>.</returns>
59-
public static IEnumerable<KeyValuePair<string, string>> PullPrefixedAttributes(
60-
this TagHelperOutput tagHelperOutput, string prefix)
108+
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
109+
/// <param name="attributes">Attributes to remove.</param>
110+
public static void RemoveRange(
111+
this TagHelperOutput tagHelperOutput, IEnumerable<KeyValuePair<string, string>> attributes)
61112
{
62-
// TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed.
63-
64-
var htmlAttributes = tagHelperOutput.Attributes;
65-
66-
// We're only interested in HTML attributes that have the desired prefix.
67-
var prefixedAttributes = htmlAttributes.Where(attribute =>
68-
attribute.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToArray();
69-
70-
// Since we're "pulling" the prefixed attribute values, we need to remove them.
71-
foreach (var attribute in prefixedAttributes)
113+
foreach (var attribute in attributes)
72114
{
73-
htmlAttributes.Remove(attribute.Key);
115+
tagHelperOutput.Attributes.Remove(attribute.Key);
74116
}
75-
76-
return prefixedAttributes;
77-
}
78-
79-
/// <summary>
80-
/// Restores a user provided bound attribute to the given <paramref name="tagHelperOutput"/>.
81-
/// </summary>
82-
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/>.</param>
83-
/// <param name="boundAttributeName">The name of the bound attribute.</param>
84-
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
85-
public static void RestoreBoundHtmlAttribute(this TagHelperOutput tagHelperOutput,
86-
string boundAttributeName,
87-
TagHelperContext context)
88-
{
89-
// We look for the original attribute so we can restore the exact attribute name the user typed.
90-
var entry = context.AllAttributes.Single(attribute =>
91-
attribute.Key.Equals(boundAttributeName, StringComparison.OrdinalIgnoreCase));
92-
var originalAttribute = new KeyValuePair<string, string>(entry.Key, entry.Value.ToString());
93-
94-
tagHelperOutput.Attributes.Add(originalAttribute);
95117
}
96118
}
97119
}

test/Microsoft.AspNet.Mvc.TagHelpers.Test/TagHelperOutputExtensionsTest.cs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class TagHelperOutputExtensionsTest
1414
[Theory]
1515
[InlineData("hello", "world")]
1616
[InlineData("HeLlO", "wOrLd")]
17-
public void RestoreBoundHtmlAttribute_RestoresOriginalAttributes(string attributeName, string attributeValue)
17+
public void CopyHtmlAttribute_CopiesOriginalAttributes(string attributeName, string attributeValue)
1818
{
1919
// Arrange
2020
var tagHelperOutput = new TagHelperOutput(
@@ -29,15 +29,42 @@ public void RestoreBoundHtmlAttribute_RestoresOriginalAttributes(string attribut
2929
var expectedAttribute = new KeyValuePair<string, string>(attributeName, attributeValue);
3030

3131
// Act
32-
tagHelperOutput.RestoreBoundHtmlAttribute("hello", tagHelperContext);
32+
tagHelperOutput.CopyHtmlAttribute("hello", tagHelperContext);
3333

3434
// Assert
3535
var attribute = Assert.Single(tagHelperOutput.Attributes);
3636
Assert.Equal(expectedAttribute, attribute);
3737
}
3838

3939
[Fact]
40-
public void PullPrefixedAttributes_OnlyRemovesPrefixed_TagHelperOutputAttributeValues()
40+
public void CopyHtmlAttribute_DoesntOverrideAttributes()
41+
{
42+
// Arrange
43+
var attributeName = "hello";
44+
var tagHelperOutput = new TagHelperOutput(
45+
"p",
46+
attributes: new Dictionary<string, string>
47+
{
48+
{ attributeName, "world2" }
49+
},
50+
content: string.Empty);
51+
var expectedAttribute = new KeyValuePair<string, string>(attributeName, "world2");
52+
var tagHelperContext = new TagHelperContext(
53+
new Dictionary<string, object>()
54+
{
55+
{ attributeName, "world" }
56+
});
57+
58+
// Act
59+
tagHelperOutput.CopyHtmlAttribute(attributeName, tagHelperContext);
60+
61+
// Assert
62+
var attribute = Assert.Single(tagHelperOutput.Attributes);
63+
Assert.Equal(expectedAttribute, attribute);
64+
}
65+
66+
[Fact]
67+
public void RemoveRange_RemovesProvidedAttributes()
4168
{
4269
// Arrange
4370
var tagHelperOutput = new TagHelperOutput(
@@ -52,19 +79,16 @@ public void PullPrefixedAttributes_OnlyRemovesPrefixed_TagHelperOutputAttributeV
5279
tagHelperOutput.Attributes.Add(expectedAttribute);
5380

5481
// Act
55-
var pulledAttributes = tagHelperOutput.PullPrefixedAttributes("route-");
82+
var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
83+
tagHelperOutput.RemoveRange(attributes);
5684

5785
// Assert
5886
var attribute = Assert.Single(tagHelperOutput.Attributes);
5987
Assert.Equal(expectedAttribute, attribute);
60-
attribute = Assert.Single(pulledAttributes, kvp => kvp.Key.Equals("route-Hello"));
61-
Assert.Equal(attribute.Value, "World");
62-
attribute = Assert.Single(pulledAttributes, kvp => kvp.Key.Equals("Route-I"));
63-
Assert.Equal(attribute.Value, "Am");
6488
}
6589

6690
[Fact]
67-
public void PullPrefixedAttributes_ReturnsEmpty_AttributeListIfNoAttributesPrefixed()
91+
public void FindPrefixedAttributes_ReturnsEmpty_AttributeListIfNoAttributesPrefixed()
6892
{
6993
// Arrange
7094
var tagHelperOutput = new TagHelperOutput(
@@ -77,10 +101,10 @@ public void PullPrefixedAttributes_ReturnsEmpty_AttributeListIfNoAttributesPrefi
77101
content: string.Empty);
78102

79103
// Act
80-
var pulledAttributes = tagHelperOutput.PullPrefixedAttributes("route-");
104+
var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
81105

82106
// Assert
83-
Assert.Empty(pulledAttributes);
107+
Assert.Empty(attributes);
84108
var attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("routeHello"));
85109
Assert.Equal(attribute.Value, "World");
86110
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("Routee-I"));

0 commit comments

Comments
 (0)