Change duplicate TagHelper bound attributes to flow to output.
- Duplicate TagHelper bound attributes used to be ignored entirely; they now flow to the output as if they were unbound. - Added code generation test to verify duplicate attributes. Added runtime and design time versions. - Updated existing tests that happened to have duplicate bound attributes. #418
This commit is contained in:
parent
039062c5eb
commit
0bb9457fd9
|
|
@ -229,15 +229,10 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
|
|||
descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attributeName)));
|
||||
|
||||
|
||||
// Bound attributes have associated descriptors.
|
||||
if (associatedDescriptors.Any())
|
||||
// Bound attributes have associated descriptors. First attribute value wins if there are duplicates;
|
||||
// later values of duplicate bound attributes are treated as if they were unbound.
|
||||
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attributeName))
|
||||
{
|
||||
if (!renderedBoundAttributeNames.Add(attributeName))
|
||||
{
|
||||
// First attribute value wins if there are duplicates.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attributeValueChunk == null)
|
||||
{
|
||||
// Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already
|
||||
|
|
|
|||
|
|
@ -879,6 +879,36 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
contentLength: 2),
|
||||
}
|
||||
},
|
||||
{
|
||||
"DuplicateAttributeTagHelpers",
|
||||
"DuplicateAttributeTagHelpers.DesignTime",
|
||||
DefaultPAndInputTagHelperDescriptors,
|
||||
new List<LineMapping>
|
||||
{
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 14,
|
||||
documentLineIndex: 0,
|
||||
generatedAbsoluteIndex: 501,
|
||||
generatedLineIndex: 15,
|
||||
characterOffsetIndex: 14,
|
||||
contentLength: 17),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 146,
|
||||
documentLineIndex: 4,
|
||||
generatedAbsoluteIndex: 1567,
|
||||
generatedLineIndex: 43,
|
||||
characterOffsetIndex: 34,
|
||||
contentLength: 4),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 43,
|
||||
documentLineIndex: 2,
|
||||
documentCharacterOffsetIndex: 8,
|
||||
generatedAbsoluteIndex: 1730,
|
||||
generatedLineIndex: 49,
|
||||
generatedCharacterOffsetIndex: 19,
|
||||
contentLength: 1),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -921,6 +951,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
"PrefixedAttributeTagHelpers.Reversed",
|
||||
PrefixedAttributeTagHelperDescriptors.Reverse()
|
||||
},
|
||||
{ "DuplicateAttributeTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class DuplicateAttributeTagHelpers
|
||||
{
|
||||
private static object @__o;
|
||||
private void @__RazorDesignTimeHelpers__()
|
||||
{
|
||||
#pragma warning disable 219
|
||||
string __tagHelperDirectiveSyntaxHelper = null;
|
||||
__tagHelperDirectiveSyntaxHelper =
|
||||
#line 1 "DuplicateAttributeTagHelpers.cshtml"
|
||||
"something, nice"
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
;
|
||||
#pragma warning restore 219
|
||||
}
|
||||
#line hidden
|
||||
private PTagHelper __PTagHelper = null;
|
||||
private InputTagHelper __InputTagHelper = null;
|
||||
private InputTagHelper2 __InputTagHelper2 = null;
|
||||
#line hidden
|
||||
public DuplicateAttributeTagHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__InputTagHelper.Type = "button";
|
||||
__InputTagHelper2.Type = __InputTagHelper.Type;
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__InputTagHelper.Type = "button";
|
||||
__InputTagHelper2.Type = __InputTagHelper.Type;
|
||||
#line 5 "DuplicateAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.Checked = true;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__PTagHelper = CreateTagHelper<PTagHelper>();
|
||||
#line 3 "DuplicateAttributeTagHelpers.cshtml"
|
||||
__PTagHelper.Age = 3;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
#pragma checksum "DuplicateAttributeTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "af64a6adaf73e4143024a1145e70cbd3a24d2c0e"
|
||||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class DuplicateAttributeTagHelpers
|
||||
{
|
||||
#line hidden
|
||||
#pragma warning disable 0414
|
||||
private TagHelperContent __tagHelperStringValueBuffer = null;
|
||||
#pragma warning restore 0414
|
||||
private TagHelperExecutionContext __tagHelperExecutionContext = null;
|
||||
private TagHelperRunner __tagHelperRunner = null;
|
||||
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
|
||||
private PTagHelper __PTagHelper = null;
|
||||
private InputTagHelper __InputTagHelper = null;
|
||||
private InputTagHelper2 __InputTagHelper2 = null;
|
||||
#line hidden
|
||||
public DuplicateAttributeTagHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
|
||||
Instrumentation.BeginContext(33, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", false, "test", async() => {
|
||||
Instrumentation.BeginContext(65, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
__InputTagHelper.Type = "button";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
|
||||
__InputTagHelper2.Type = __InputTagHelper.Type;
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("TYPE", Html.Raw("checkbox"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(71, 39, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(110, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
__InputTagHelper.Type = "button";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
|
||||
__InputTagHelper2.Type = __InputTagHelper.Type;
|
||||
#line 5 "DuplicateAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.Checked = true;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("checkbox"));
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("checked", Html.Raw("false"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(116, 70, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(186, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__PTagHelper = CreateTagHelper<PTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__PTagHelper);
|
||||
#line 3 "DuplicateAttributeTagHelpers.cshtml"
|
||||
__PTagHelper.Age = 3;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("AGE", Html.Raw("40"));
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("Age", Html.Raw("500"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(35, 157, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -166,6 +166,7 @@ __InputTagHelper2.IntDictionaryProperty["grabber"] = 42;
|
|||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-pepper", __InputTagHelper2.IntDictionaryProperty["pepper"]);
|
||||
__InputTagHelper1.IntDictionaryProperty["pepper"] = __InputTagHelper2.IntDictionaryProperty["pepper"];
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("int-prefix-salt", Html.Raw("8"));
|
||||
__InputTagHelper2.StringDictionaryProperty["grabber"] = "string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-grabber", __InputTagHelper2.StringDictionaryProperty["grabber"]);
|
||||
__InputTagHelper1.StringProperty = __InputTagHelper2.StringDictionaryProperty["grabber"];
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ __InputTagHelper1.IntProperty = 42;
|
|||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-pepper", __InputTagHelper1.IntDictionaryProperty["pepper"]);
|
||||
__InputTagHelper2.IntDictionaryProperty["pepper"] = __InputTagHelper1.IntDictionaryProperty["pepper"];
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("int-prefix-salt", Html.Raw("8"));
|
||||
__InputTagHelper1.StringProperty = "string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-grabber", __InputTagHelper1.StringProperty);
|
||||
__InputTagHelper2.StringDictionaryProperty["grabber"] = __InputTagHelper1.StringProperty;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
@addTagHelper "something, nice"
|
||||
|
||||
<p age="3" AGE="40" Age="500">
|
||||
<input type="button" TYPE="checkbox" />
|
||||
<input type="button" checked="true" type="checkbox" checked="false" />
|
||||
</p>
|
||||
Loading…
Reference in New Issue