parent
a80594d706
commit
87bb1d0ff5
|
|
@ -194,8 +194,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
break;
|
||||
|
||||
case "checkbox":
|
||||
GenerateCheckBox(modelExplorer, output);
|
||||
return;
|
||||
tagBuilder = GenerateCheckBox(modelExplorer, output);
|
||||
break;
|
||||
|
||||
case "password":
|
||||
tagBuilder = Generator.GeneratePassword(
|
||||
|
|
@ -252,7 +252,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
return inputTypeHint;
|
||||
}
|
||||
|
||||
private void GenerateCheckBox(ModelExplorer modelExplorer, TagHelperOutput output)
|
||||
private TagBuilder GenerateCheckBox(ModelExplorer modelExplorer, TagHelperOutput output)
|
||||
{
|
||||
if (modelExplorer.ModelType == typeof(string))
|
||||
{
|
||||
|
|
@ -280,53 +280,30 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
"checkbox"));
|
||||
}
|
||||
|
||||
// Prepare to move attributes from current element to <input type="checkbox"/> generated just below.
|
||||
var htmlAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Perf: Avoid allocating enumerator
|
||||
// Construct attributes correctly (first attribute wins).
|
||||
for (var i = 0; i < output.Attributes.Count; i++)
|
||||
// hiddenForCheckboxTag always rendered after the returned element
|
||||
var hiddenForCheckboxTag = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name);
|
||||
if (hiddenForCheckboxTag != null)
|
||||
{
|
||||
var attribute = output.Attributes[i];
|
||||
if (!htmlAttributes.ContainsKey(attribute.Name))
|
||||
var renderingMode =
|
||||
output.TagMode == TagMode.SelfClosing ? TagRenderMode.SelfClosing : TagRenderMode.StartTag;
|
||||
hiddenForCheckboxTag.TagRenderMode = renderingMode;
|
||||
|
||||
if (ViewContext.FormContext.CanRenderAtEndOfForm)
|
||||
{
|
||||
htmlAttributes.Add(attribute.Name, attribute.Value);
|
||||
ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckboxTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.PostElement.AppendHtml(hiddenForCheckboxTag);
|
||||
}
|
||||
}
|
||||
|
||||
var checkBoxTag = Generator.GenerateCheckBox(
|
||||
return Generator.GenerateCheckBox(
|
||||
ViewContext,
|
||||
modelExplorer,
|
||||
For.Name,
|
||||
isChecked: null,
|
||||
htmlAttributes: htmlAttributes);
|
||||
if (checkBoxTag != null)
|
||||
{
|
||||
// Do not generate current element's attributes or tags. Instead put both <input type="checkbox"/> and
|
||||
// <input type="hidden"/> into the output's Content.
|
||||
output.Attributes.Clear();
|
||||
output.TagName = null;
|
||||
|
||||
var renderingMode =
|
||||
output.TagMode == TagMode.SelfClosing ? TagRenderMode.SelfClosing : TagRenderMode.StartTag;
|
||||
checkBoxTag.TagRenderMode = renderingMode;
|
||||
output.Content.AppendHtml(checkBoxTag);
|
||||
|
||||
var hiddenForCheckboxTag = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name);
|
||||
if (hiddenForCheckboxTag != null)
|
||||
{
|
||||
hiddenForCheckboxTag.TagRenderMode = renderingMode;
|
||||
|
||||
if (ViewContext.FormContext.CanRenderAtEndOfForm)
|
||||
{
|
||||
ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckboxTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Content.AppendHtml(hiddenForCheckboxTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
htmlAttributes: null);
|
||||
}
|
||||
|
||||
private TagBuilder GenerateRadio(ModelExplorer modelExplorer)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<label class="order" for="HtmlEncode[[NeedSpecialHandle]]">HtmlEncode[[NeedSpecialHandle]]</label>
|
||||
<input checked="HtmlEncode[[checked]]" data-val="HtmlEncode[[true]]" data-val-required="HtmlEncode[[The NeedSpecialHandle field is required.]]" id="HtmlEncode[[NeedSpecialHandle]]" name="HtmlEncode[[NeedSpecialHandle]]" type="HtmlEncode[[checkbox]]" value="HtmlEncode[[true]]" />
|
||||
<input type="HtmlEncode[[checkbox]]" checked="HtmlEncode[[checked]]" data-val="HtmlEncode[[true]]" data-val-required="HtmlEncode[[The NeedSpecialHandle field is required.]]" id="HtmlEncode[[NeedSpecialHandle]]" name="HtmlEncode[[NeedSpecialHandle]]" value="HtmlEncode[[true]]" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="order" for="HtmlEncode[[PaymentMethod]]">HtmlEncode[[PaymentMethod]]</label>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<label class="order" for="NeedSpecialHandle">NeedSpecialHandle</label>
|
||||
<input checked="checked" data-val="true" data-val-required="The NeedSpecialHandle field is required." id="NeedSpecialHandle" name="NeedSpecialHandle" type="checkbox" value="true" />
|
||||
<input type="checkbox" checked="checked" data-val="true" data-val-required="The NeedSpecialHandle field is required." id="NeedSpecialHandle" name="NeedSpecialHandle" value="true" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="order" for="PaymentMethod">PaymentMethod</label>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "hello", "world" },
|
||||
{ "hello", "world2" }
|
||||
},
|
||||
"hello=\"HtmlEncode[[world]]\""
|
||||
"hello=\"HtmlEncode[[world]]\" hello=\"HtmlEncode[[world2]]\""
|
||||
},
|
||||
{
|
||||
new TagHelperAttributeList
|
||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "hello", "world2" },
|
||||
{ "hello", "world3" }
|
||||
},
|
||||
"hello=\"HtmlEncode[[world]]\""
|
||||
"hello=\"HtmlEncode[[world]]\" hello=\"HtmlEncode[[world2]]\" hello=\"HtmlEncode[[world3]]\""
|
||||
},
|
||||
{
|
||||
new TagHelperAttributeList
|
||||
|
|
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "HelLO", "world" },
|
||||
{ "HELLO", "world2" }
|
||||
},
|
||||
"HelLO=\"HtmlEncode[[world]]\""
|
||||
"HelLO=\"HtmlEncode[[world]]\" HELLO=\"HtmlEncode[[world2]]\""
|
||||
},
|
||||
{
|
||||
new TagHelperAttributeList
|
||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "HELLO", "world2" },
|
||||
{ "hello", "world3" }
|
||||
},
|
||||
"Hello=\"HtmlEncode[[world]]\""
|
||||
"Hello=\"HtmlEncode[[world]]\" HELLO=\"HtmlEncode[[world2]]\" hello=\"HtmlEncode[[world3]]\""
|
||||
},
|
||||
{
|
||||
new TagHelperAttributeList
|
||||
|
|
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "HeLlO", "world" },
|
||||
{ "hello", "world2" }
|
||||
},
|
||||
"HeLlO=\"HtmlEncode[[world]]\""
|
||||
"HeLlO=\"HtmlEncode[[world]]\" hello=\"HtmlEncode[[world2]]\""
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -76,15 +76,14 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
|
||||
[Theory]
|
||||
[MemberData(nameof(MultiAttributeCheckBoxData))]
|
||||
public async Task CheckBoxHandlesMultipleAttributesSameNameCorrectly(
|
||||
public async Task CheckBoxHandlesMultipleAttributesSameNameArePreserved(
|
||||
TagHelperAttributeList outputAttributes,
|
||||
string expectedAttributeString)
|
||||
{
|
||||
// Arrange
|
||||
var originalContent = "original content";
|
||||
var originalTagName = "not-input";
|
||||
var expectedContent = $"{originalContent}<input {expectedAttributeString} id=\"HtmlEncode[[IsACar]]\" " +
|
||||
"name=\"HtmlEncode[[IsACar]]\" type=\"HtmlEncode[[checkbox]]\" value=\"HtmlEncode[[true]]\" />" +
|
||||
var expectedContent = $"<input {expectedAttributeString} type=\"HtmlEncode[[checkbox]]\" id=\"HtmlEncode[[IsACar]]\" " +
|
||||
$"name=\"HtmlEncode[[IsACar]]\" value=\"HtmlEncode[[true]]\" />" +
|
||||
"<input name=\"HtmlEncode[[IsACar]]\" type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[false]]\" />";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
|
|
@ -94,12 +93,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
originalTagName,
|
||||
"input",
|
||||
outputAttributes,
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
|
||||
{
|
||||
TagMode = TagMode.SelfClosing,
|
||||
};
|
||||
|
||||
output.Content.AppendHtml(originalContent);
|
||||
var htmlGenerator = new TestableHtmlGenerator(new EmptyModelMetadataProvider());
|
||||
var tagHelper = GetTagHelper(htmlGenerator, model: false, propertyName: nameof(Model.IsACar));
|
||||
|
|
@ -108,10 +108,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
await tagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(output.Attributes); // Moved to Content and cleared
|
||||
Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output.Content));
|
||||
Assert.Equal(TagMode.SelfClosing, output.TagMode);
|
||||
Assert.Null(output.TagName); // Cleared
|
||||
Assert.NotNull(output.PostElement);
|
||||
Assert.Equal(originalContent, HtmlContentUtilities.HtmlContentToString(output.Content));
|
||||
Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -213,13 +212,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
// Arrange
|
||||
const string content = "original content";
|
||||
const string tagName = "input";
|
||||
const string isCheckedAttr = " checked=\"HtmlEncode[[checked]]\"";
|
||||
const string isCheckedAttr = "checked=\"HtmlEncode[[checked]]\" ";
|
||||
var isChecked = (bool.Parse(possibleBool) ? isCheckedAttr : string.Empty);
|
||||
var expectedContent = $"{content}<input{isChecked} class=\"HtmlEncode[[form-control]]\" " +
|
||||
"id=\"HtmlEncode[[IsACar]]\" name=\"HtmlEncode[[IsACar]]\" type=\"HtmlEncode[[checkbox]]\" " +
|
||||
var expectedContent = $"<input class=\"HtmlEncode[[form-control]]\" type=\"HtmlEncode[[checkbox]]\" " +
|
||||
$"{isChecked}id=\"HtmlEncode[[IsACar]]\" name=\"HtmlEncode[[IsACar]]\" " +
|
||||
"value=\"HtmlEncode[[true]]\" /><input name=\"HtmlEncode[[IsACar]]\" type=\"HtmlEncode[[hidden]]\" " +
|
||||
"value=\"HtmlEncode[[false]]\" />";
|
||||
|
||||
var expectedPostElement = "<input name=\"IsACar\" type=\"hidden\" value=\"false\" />";
|
||||
|
||||
var attributes = new TagHelperAttributeList
|
||||
{
|
||||
|
|
@ -245,12 +244,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
|
||||
// Act
|
||||
tagHelper.Process(context, output);
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.Empty(output.Attributes);
|
||||
Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output.Content));
|
||||
Assert.Equal(TagMode.SelfClosing, output.TagMode);
|
||||
Assert.Null(output.TagName);
|
||||
Assert.Equal(content, HtmlContentUtilities.HtmlContentToString(output.Content));
|
||||
Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output));
|
||||
Assert.Equal(expectedPostElement, output.PostElement.GetContent());
|
||||
}
|
||||
|
||||
// Top-level container (List<Model> or Model instance), immediate container type (Model or NestModel),
|
||||
|
|
@ -464,10 +462,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{
|
||||
// Arrange
|
||||
var originalContent = "original content";
|
||||
var originalTagName = "not-input";
|
||||
var expectedPreContent = "original pre-content";
|
||||
var expectedContent = originalContent + "<input class=\"HtmlEncode[[form-control]]\" /><hidden />";
|
||||
var expectedContent = "<input class=\"HtmlEncode[[form-control]]\" type=\"HtmlEncode[[checkbox]]\" /><hidden />";
|
||||
var expectedPostContent = "original post-content";
|
||||
var expectedPostElement = "<hidden />";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
tagName: "input",
|
||||
|
|
@ -480,7 +478,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "class", "form-control" },
|
||||
};
|
||||
var output = new TagHelperOutput(
|
||||
originalTagName,
|
||||
"input",
|
||||
originalAttributes,
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
|
|
@ -501,10 +499,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
|
||||
var tagBuilder = new TagBuilder("input")
|
||||
{
|
||||
Attributes =
|
||||
{
|
||||
{ "class", "form-control" },
|
||||
},
|
||||
TagRenderMode = TagRenderMode.SelfClosing
|
||||
};
|
||||
htmlGenerator
|
||||
|
|
@ -530,12 +524,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
// Assert
|
||||
htmlGenerator.Verify();
|
||||
|
||||
Assert.Empty(output.Attributes); // Moved to Content and cleared
|
||||
Assert.NotEmpty(output.Attributes);
|
||||
Assert.Equal(expectedPreContent, output.PreContent.GetContent());
|
||||
Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output.Content));
|
||||
Assert.Equal(originalContent, HtmlContentUtilities.HtmlContentToString(output.Content));
|
||||
Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output));
|
||||
Assert.Equal(expectedPostContent, output.PostContent.GetContent());
|
||||
Assert.Equal(expectedPostElement, output.PostElement.GetContent());
|
||||
Assert.Equal(TagMode.SelfClosing, output.TagMode);
|
||||
Assert.Null(output.TagName); // Cleared
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
Loading…
Reference in New Issue