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