Mgbbs/hidden for checkbox render mode (#13014)
* Added CheckBoxHiddenInputRenderMode to HtmlHelperOptions, ViewContext, and html/tag helpers Fixes #12833
This commit is contained in:
parent
a88180472d
commit
90e89e9708
|
|
@ -309,29 +309,32 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
"checkbox"));
|
"checkbox"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// hiddenForCheckboxTag always rendered after the returned element
|
if (ViewContext.CheckBoxHiddenInputRenderMode != CheckBoxHiddenInputRenderMode.None)
|
||||||
var hiddenForCheckboxTag = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name);
|
|
||||||
if (hiddenForCheckboxTag != null)
|
|
||||||
{
|
{
|
||||||
var renderingMode =
|
// hiddenForCheckboxTag always rendered after the returned element
|
||||||
output.TagMode == TagMode.SelfClosing ? TagRenderMode.SelfClosing : TagRenderMode.StartTag;
|
var hiddenForCheckboxTag = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name);
|
||||||
hiddenForCheckboxTag.TagRenderMode = renderingMode;
|
if (hiddenForCheckboxTag != null)
|
||||||
if (!hiddenForCheckboxTag.Attributes.ContainsKey("name") &&
|
|
||||||
!string.IsNullOrEmpty(Name))
|
|
||||||
{
|
{
|
||||||
// The checkbox and hidden elements should have the same name attribute value. Attributes will
|
var renderingMode =
|
||||||
// match if both are present because both have a generated value. Reach here in the special case
|
output.TagMode == TagMode.SelfClosing ? TagRenderMode.SelfClosing : TagRenderMode.StartTag;
|
||||||
// where user provided a non-empty fallback name.
|
hiddenForCheckboxTag.TagRenderMode = renderingMode;
|
||||||
hiddenForCheckboxTag.MergeAttribute("name", Name);
|
if (!hiddenForCheckboxTag.Attributes.ContainsKey("name") &&
|
||||||
}
|
!string.IsNullOrEmpty(Name))
|
||||||
|
{
|
||||||
|
// The checkbox and hidden elements should have the same name attribute value. Attributes will
|
||||||
|
// match if both are present because both have a generated value. Reach here in the special case
|
||||||
|
// where user provided a non-empty fallback name.
|
||||||
|
hiddenForCheckboxTag.MergeAttribute("name", Name);
|
||||||
|
}
|
||||||
|
|
||||||
if (ViewContext.FormContext.CanRenderAtEndOfForm)
|
if (ViewContext.CheckBoxHiddenInputRenderMode == CheckBoxHiddenInputRenderMode.EndOfForm && ViewContext.FormContext.CanRenderAtEndOfForm)
|
||||||
{
|
{
|
||||||
ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckboxTag);
|
ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckboxTag);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
output.PostElement.AppendHtml(hiddenForCheckboxTag);
|
output.PostElement.AppendHtml(hiddenForCheckboxTag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -838,6 +838,243 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
Assert.Equal(expectedTagName, output.TagName);
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeNone()
|
||||||
|
{
|
||||||
|
var propertyName = "-expression-";
|
||||||
|
var expectedTagName = "input";
|
||||||
|
var inputTypeName = "checkbox";
|
||||||
|
var expectedAttributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
{ "value", "true" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||||
|
var model = false;
|
||||||
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model);
|
||||||
|
var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer);
|
||||||
|
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||||
|
|
||||||
|
viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.None;
|
||||||
|
|
||||||
|
var tagHelper = new InputTagHelper(htmlGenerator)
|
||||||
|
{
|
||||||
|
For = modelExpression,
|
||||||
|
InputTypeName = inputTypeName,
|
||||||
|
Name = propertyName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
var attributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new TagHelperContext(attributes, new Dictionary<object, object>(), "test");
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
expectedTagName,
|
||||||
|
new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
|
||||||
|
{
|
||||||
|
TagMode = TagMode.SelfClosing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.False(output.IsContentModified);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
|
||||||
|
Assert.False(viewContext.FormContext.HasEndOfFormContent);
|
||||||
|
Assert.True(string.IsNullOrEmpty(HtmlContentUtilities.HtmlContentToString(output.PostElement)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeInline()
|
||||||
|
{
|
||||||
|
var propertyName = "-expression-";
|
||||||
|
var expectedTagName = "input";
|
||||||
|
var expectedPostElementContent = $"<input name=\"HtmlEncode[[{propertyName}]]\" " +
|
||||||
|
"type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[false]]\" />";
|
||||||
|
var inputTypeName = "checkbox";
|
||||||
|
var expectedAttributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
{ "value", "true" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||||
|
var model = false;
|
||||||
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model);
|
||||||
|
var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer);
|
||||||
|
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||||
|
|
||||||
|
viewContext.FormContext.CanRenderAtEndOfForm = true;
|
||||||
|
viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.Inline;
|
||||||
|
|
||||||
|
var tagHelper = new InputTagHelper(htmlGenerator)
|
||||||
|
{
|
||||||
|
For = modelExpression,
|
||||||
|
InputTypeName = inputTypeName,
|
||||||
|
Name = propertyName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
var attributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new TagHelperContext(attributes, new Dictionary<object, object>(), "test");
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
expectedTagName,
|
||||||
|
new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
|
||||||
|
{
|
||||||
|
TagMode = TagMode.SelfClosing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.False(output.IsContentModified);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
|
||||||
|
Assert.False(viewContext.FormContext.HasEndOfFormContent);
|
||||||
|
Assert.Equal(expectedPostElementContent, HtmlContentUtilities.HtmlContentToString(output.PostElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeEndOfForm()
|
||||||
|
{
|
||||||
|
var propertyName = "-expression-";
|
||||||
|
var expectedTagName = "input";
|
||||||
|
var expectedEndOfFormContent = $"<input name=\"HtmlEncode[[{propertyName}]]\" " +
|
||||||
|
"type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[false]]\" />";
|
||||||
|
var inputTypeName = "checkbox";
|
||||||
|
var expectedAttributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
{ "value", "true" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||||
|
var model = false;
|
||||||
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model);
|
||||||
|
var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer);
|
||||||
|
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||||
|
|
||||||
|
viewContext.FormContext.CanRenderAtEndOfForm = true;
|
||||||
|
viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm;
|
||||||
|
|
||||||
|
var tagHelper = new InputTagHelper(htmlGenerator)
|
||||||
|
{
|
||||||
|
For = modelExpression,
|
||||||
|
InputTypeName = inputTypeName,
|
||||||
|
Name = propertyName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
var attributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new TagHelperContext(attributes, new Dictionary<object, object>(), "test");
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
expectedTagName,
|
||||||
|
new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
|
||||||
|
{
|
||||||
|
TagMode = TagMode.SelfClosing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.False(output.IsContentModified);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
|
||||||
|
Assert.Equal(expectedEndOfFormContent, string.Join("", viewContext.FormContext.EndOfFormContent.Select(html => HtmlContentUtilities.HtmlContentToString(html))));
|
||||||
|
Assert.True(string.IsNullOrEmpty(HtmlContentUtilities.HtmlContentToString(output.PostElement)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeEndOfForm_AndCanRenderAtEndOfFormNotSet()
|
||||||
|
{
|
||||||
|
var propertyName = "-expression-";
|
||||||
|
var expectedTagName = "input";
|
||||||
|
var expectedPostElementContent = $"<input name=\"HtmlEncode[[{propertyName}]]\" " +
|
||||||
|
"type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[false]]\" />";
|
||||||
|
var inputTypeName = "checkbox";
|
||||||
|
var expectedAttributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
{ "value", "true" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||||
|
var model = false;
|
||||||
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model);
|
||||||
|
var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer);
|
||||||
|
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||||
|
|
||||||
|
viewContext.FormContext.CanRenderAtEndOfForm = false;
|
||||||
|
viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm;
|
||||||
|
|
||||||
|
var tagHelper = new InputTagHelper(htmlGenerator)
|
||||||
|
{
|
||||||
|
For = modelExpression,
|
||||||
|
InputTypeName = inputTypeName,
|
||||||
|
Name = propertyName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
var attributes = new TagHelperAttributeList
|
||||||
|
{
|
||||||
|
{ "name", propertyName },
|
||||||
|
{ "type", inputTypeName },
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new TagHelperContext(attributes, new Dictionary<object, object>(), "test");
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
expectedTagName,
|
||||||
|
new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
|
||||||
|
{
|
||||||
|
TagMode = TagMode.SelfClosing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.False(output.IsContentModified);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
|
||||||
|
Assert.False(viewContext.FormContext.HasEndOfFormContent);
|
||||||
|
Assert.Equal(expectedPostElementContent, HtmlContentUtilities.HtmlContentToString(output.PostElement));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ProcessAsync_CallsGenerateCheckBox_WithExpectedParameters()
|
public async Task ProcessAsync_CallsGenerateCheckBox_WithExpectedParameters()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
namespace Microsoft.AspNetCore.Mvc.Rendering
|
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
{
|
{
|
||||||
|
public enum CheckBoxHiddenInputRenderMode
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Inline = 1,
|
||||||
|
EndOfForm = 2,
|
||||||
|
}
|
||||||
public enum FormMethod
|
public enum FormMethod
|
||||||
{
|
{
|
||||||
Get = 0,
|
Get = 0,
|
||||||
|
|
@ -678,6 +684,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
public ViewContext() { }
|
public ViewContext() { }
|
||||||
public ViewContext(Microsoft.AspNetCore.Mvc.ActionContext actionContext, Microsoft.AspNetCore.Mvc.ViewEngines.IView view, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData, Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionary tempData, System.IO.TextWriter writer, Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelperOptions htmlHelperOptions) { }
|
public ViewContext(Microsoft.AspNetCore.Mvc.ActionContext actionContext, Microsoft.AspNetCore.Mvc.ViewEngines.IView view, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData, Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionary tempData, System.IO.TextWriter writer, Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelperOptions htmlHelperOptions) { }
|
||||||
public ViewContext(Microsoft.AspNetCore.Mvc.Rendering.ViewContext viewContext, Microsoft.AspNetCore.Mvc.ViewEngines.IView view, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData, System.IO.TextWriter writer) { }
|
public ViewContext(Microsoft.AspNetCore.Mvc.Rendering.ViewContext viewContext, Microsoft.AspNetCore.Mvc.ViewEngines.IView view, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData, System.IO.TextWriter writer) { }
|
||||||
|
public Microsoft.AspNetCore.Mvc.Rendering.CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public bool ClientValidationEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public bool ClientValidationEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string ExecutingFilePath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string ExecutingFilePath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public virtual Microsoft.AspNetCore.Mvc.ViewFeatures.FormContext FormContext { get { throw null; } set { } }
|
public virtual Microsoft.AspNetCore.Mvc.ViewFeatures.FormContext FormContext { get { throw null; } set { } }
|
||||||
|
|
@ -1062,6 +1069,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
public partial class HtmlHelperOptions
|
public partial class HtmlHelperOptions
|
||||||
{
|
{
|
||||||
public HtmlHelperOptions() { }
|
public HtmlHelperOptions() { }
|
||||||
|
public Microsoft.AspNetCore.Mvc.Rendering.CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public bool ClientValidationEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public bool ClientValidationEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public Microsoft.AspNetCore.Mvc.Rendering.Html5DateRenderingMode Html5DateRenderingMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public Microsoft.AspNetCore.Mvc.Rendering.Html5DateRenderingMode Html5DateRenderingMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string IdAttributeDotReplacement { get { throw null; } set { } }
|
public string IdAttributeDotReplacement { get { throw null; } set { } }
|
||||||
|
|
|
||||||
|
|
@ -721,8 +721,18 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
isChecked,
|
isChecked,
|
||||||
htmlAttributes);
|
htmlAttributes);
|
||||||
|
|
||||||
|
if (checkbox == null)
|
||||||
|
{
|
||||||
|
return HtmlString.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ViewContext.CheckBoxHiddenInputRenderMode == CheckBoxHiddenInputRenderMode.None)
|
||||||
|
{
|
||||||
|
return checkbox;
|
||||||
|
}
|
||||||
|
|
||||||
var hiddenForCheckbox = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, expression);
|
var hiddenForCheckbox = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, expression);
|
||||||
if (checkbox == null || hiddenForCheckbox == null)
|
if (hiddenForCheckbox == null)
|
||||||
{
|
{
|
||||||
return HtmlString.Empty;
|
return HtmlString.Empty;
|
||||||
}
|
}
|
||||||
|
|
@ -736,7 +746,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
hiddenForCheckbox.MergeAttribute("name", name);
|
hiddenForCheckbox.MergeAttribute("name", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ViewContext.FormContext.CanRenderAtEndOfForm)
|
if (ViewContext.CheckBoxHiddenInputRenderMode == CheckBoxHiddenInputRenderMode.EndOfForm && ViewContext.FormContext.CanRenderAtEndOfForm)
|
||||||
{
|
{
|
||||||
ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckbox);
|
ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckbox);
|
||||||
return checkbox;
|
return checkbox;
|
||||||
|
|
|
||||||
|
|
@ -56,5 +56,10 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
/// <see cref="IHtmlHelper.ValidationSummary"/> and other overloads.
|
/// <see cref="IHtmlHelper.ValidationSummary"/> and other overloads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ValidationSummaryMessageElement { get; set; } = "span";
|
public string ValidationSummaryMessageElement { get; set; } = "span";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the way hidden inputs are rendered for checkbox tag helpers and html helpers.
|
||||||
|
/// </summary>
|
||||||
|
public CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { get; set; } = CheckBoxHiddenInputRenderMode.EndOfForm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Controls the rendering of hidden input fields when using CheckBox tag helpers or html helpers.
|
||||||
|
/// </summary>
|
||||||
|
public enum CheckBoxHiddenInputRenderMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hidden input fields will not be automatically rendered. If checkbox is not checked, no value will be posted.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hidden input fields will be rendered inline with each checkbox. Use this for legacy ASP.NET MVC behavior.
|
||||||
|
/// </summary>
|
||||||
|
Inline = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hidden input fields will be rendered for each checkbox at the bottom of the form element. This is the preferred render method and default MVC behavior.
|
||||||
|
/// If <see cref="Microsoft.AspNetCore.Mvc.ViewFeatures.FormContext.CanRenderAtEndOfForm"/> is <c>false</c>, will fall back on <see cref="Inline"/>.
|
||||||
|
/// </summary>
|
||||||
|
EndOfForm = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -87,6 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
Html5DateRenderingMode = htmlHelperOptions.Html5DateRenderingMode;
|
Html5DateRenderingMode = htmlHelperOptions.Html5DateRenderingMode;
|
||||||
ValidationSummaryMessageElement = htmlHelperOptions.ValidationSummaryMessageElement;
|
ValidationSummaryMessageElement = htmlHelperOptions.ValidationSummaryMessageElement;
|
||||||
ValidationMessageElement = htmlHelperOptions.ValidationMessageElement;
|
ValidationMessageElement = htmlHelperOptions.ValidationMessageElement;
|
||||||
|
CheckBoxHiddenInputRenderMode = htmlHelperOptions.CheckBoxHiddenInputRenderMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -129,6 +130,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
Html5DateRenderingMode = viewContext.Html5DateRenderingMode;
|
Html5DateRenderingMode = viewContext.Html5DateRenderingMode;
|
||||||
ValidationSummaryMessageElement = viewContext.ValidationSummaryMessageElement;
|
ValidationSummaryMessageElement = viewContext.ValidationSummaryMessageElement;
|
||||||
ValidationMessageElement = viewContext.ValidationMessageElement;
|
ValidationMessageElement = viewContext.ValidationMessageElement;
|
||||||
|
CheckBoxHiddenInputRenderMode = viewContext.CheckBoxHiddenInputRenderMode;
|
||||||
|
|
||||||
ExecutingFilePath = viewContext.ExecutingFilePath;
|
ExecutingFilePath = viewContext.ExecutingFilePath;
|
||||||
View = view;
|
View = view;
|
||||||
|
|
@ -180,6 +182,11 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ValidationMessageElement { get; set; }
|
public string ValidationMessageElement { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the way hidden inputs are rendered for checkbox tag helpers and html helpers.
|
||||||
|
/// </summary>
|
||||||
|
public CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the dynamic view bag.
|
/// Gets the dynamic view bag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,93 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
writer.ToString());
|
writer.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckBox_WithHiddenInputRenderModeNone_DoesNotGenerateHiddenInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean");
|
||||||
|
var expected = @"<input checked=""HtmlEncode[[checked]]"" data-val=""HtmlEncode[[true]]"" " +
|
||||||
|
$@"data-val-required=""HtmlEncode[[{requiredMessage}]]"" id=""HtmlEncode[[Property1]]"" " +
|
||||||
|
@"name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[checkbox]]"" " +
|
||||||
|
@"value=""HtmlEncode[[true]]"" />";
|
||||||
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData());
|
||||||
|
helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.None;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(helper.ViewContext.FormContext.HasEndOfFormContent);
|
||||||
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckBox_WithHiddenInputRenderModeInline_GeneratesHiddenInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean");
|
||||||
|
var expected = @"<input checked=""HtmlEncode[[checked]]"" data-val=""HtmlEncode[[true]]"" " +
|
||||||
|
$@"data-val-required=""HtmlEncode[[{requiredMessage}]]"" id=""HtmlEncode[[Property1]]"" " +
|
||||||
|
@"name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[checkbox]]"" " +
|
||||||
|
@"value=""HtmlEncode[[true]]"" /><input name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[hidden]]"" value=""HtmlEncode[[false]]"" />";
|
||||||
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData());
|
||||||
|
helper.ViewContext.FormContext.CanRenderAtEndOfForm = true;
|
||||||
|
helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.Inline;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckBox_WithHiddenInputRenderModeEndOfForm_GeneratesHiddenInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean");
|
||||||
|
var expected = @"<input checked=""HtmlEncode[[checked]]"" data-val=""HtmlEncode[[true]]"" " +
|
||||||
|
$@"data-val-required=""HtmlEncode[[{requiredMessage}]]"" id=""HtmlEncode[[Property1]]"" " +
|
||||||
|
@"name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[checkbox]]"" " +
|
||||||
|
@"value=""HtmlEncode[[true]]"" />";
|
||||||
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData());
|
||||||
|
helper.ViewContext.FormContext.CanRenderAtEndOfForm = true;
|
||||||
|
helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(helper.ViewContext.FormContext.HasEndOfFormContent);
|
||||||
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html));
|
||||||
|
var writer = new StringWriter();
|
||||||
|
var hiddenTag = Assert.Single(helper.ViewContext.FormContext.EndOfFormContent);
|
||||||
|
hiddenTag.WriteTo(writer, new HtmlTestEncoder());
|
||||||
|
Assert.Equal("<input name=\"HtmlEncode[[Property1]]\" type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[false]]\" />",
|
||||||
|
writer.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckBox_WithHiddenInputRenderModeEndOfForm_WithCanRenderAtEndOfFormNotSet_GeneratesHiddenInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean");
|
||||||
|
var expected = @"<input checked=""HtmlEncode[[checked]]"" data-val=""HtmlEncode[[true]]"" " +
|
||||||
|
$@"data-val-required=""HtmlEncode[[{requiredMessage}]]"" id=""HtmlEncode[[Property1]]"" " +
|
||||||
|
@"name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[checkbox]]"" " +
|
||||||
|
@"value=""HtmlEncode[[true]]"" /><input name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[hidden]]"" value=""HtmlEncode[[false]]"" />";
|
||||||
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData());
|
||||||
|
helper.ViewContext.FormContext.CanRenderAtEndOfForm = false;
|
||||||
|
helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(helper.ViewContext.FormContext.HasEndOfFormContent);
|
||||||
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CheckBoxUsesAttemptedValueFromModelState()
|
public void CheckBoxUsesAttemptedValueFromModelState()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue