diff --git a/src/Components/Web/src/Forms/InputHidden.cs b/src/Components/Web/src/Forms/InputHidden.cs new file mode 100644 index 000000000000..d2f95d1b6272 --- /dev/null +++ b/src/Components/Web/src/Forms/InputHidden.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components.Rendering; + +namespace Microsoft.AspNetCore.Components.Forms; + +/// +/// An hidden input component for storing values. +/// +public class InputHidden : InputBase +{ + /// + /// Gets or sets the associated . + /// + /// May be if accessed before the component is rendered. + /// + /// + [DisallowNull] public ElementReference? Element { get; protected set; } + + /// + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, "input"); + builder.AddAttribute(1, "type", "hidden"); + builder.AddMultipleAttributes(2, AdditionalAttributes); + builder.AddAttributeIfNotNullOrEmpty(3, "name", NameAttributeValue); + builder.AddAttributeIfNotNullOrEmpty(4, "class", CssClass); + builder.AddAttribute(5, "value", CurrentValueAsString); + builder.AddAttribute(6, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString)); + builder.SetUpdatesAttributeName("value"); + builder.AddElementReferenceCapture(7, __inputReference => Element = __inputReference); + builder.CloseElement(); + } + + /// + protected override bool TryParseValueFromString(string? value, out string? result, [NotNullWhen(false)] out string? validationErrorMessage) + { + result = value; + validationErrorMessage = null; + return true; + } +} diff --git a/src/Components/Web/src/PublicAPI.Unshipped.txt b/src/Components/Web/src/PublicAPI.Unshipped.txt index 99365e10804e..5b85eaf45fdc 100644 --- a/src/Components/Web/src/PublicAPI.Unshipped.txt +++ b/src/Components/Web/src/PublicAPI.Unshipped.txt @@ -1,3 +1,9 @@ #nullable enable +Microsoft.AspNetCore.Components.Forms.InputHidden +Microsoft.AspNetCore.Components.Forms.InputHidden.Element.get -> Microsoft.AspNetCore.Components.ElementReference? +Microsoft.AspNetCore.Components.Forms.InputHidden.Element.set -> void +Microsoft.AspNetCore.Components.Forms.InputHidden.InputHidden() -> void Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> string! +override Microsoft.AspNetCore.Components.Forms.InputHidden.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void +override Microsoft.AspNetCore.Components.Forms.InputHidden.TryParseValueFromString(string? value, out string? result, out string? validationErrorMessage) -> bool virtual Microsoft.AspNetCore.Components.Routing.NavLink.ShouldMatch(string! uriAbsolute) -> bool \ No newline at end of file diff --git a/src/Components/Web/test/Forms/InputHiddenTest.cs b/src/Components/Web/test/Forms/InputHiddenTest.cs new file mode 100644 index 000000000000..73cf7d5414f8 --- /dev/null +++ b/src/Components/Web/test/Forms/InputHiddenTest.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Components.Forms; + +public class InputHiddenTest +{ + [Fact] + public async Task InputElementIsAssignedSuccessfully() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestInputHostComponent + { + EditContext = new EditContext(model), + ValueExpression = () => model.StringProperty, + }; + + // Act + var inputHiddenComponent = await InputRenderer.RenderAndGetComponent(rootComponent); + + // Assert + Assert.NotNull(inputHiddenComponent.Element); + } + + private class TestModel + { + public string StringProperty { get; set; } + } +} diff --git a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs index dce828fcefbf..b6948f9b766c 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs @@ -104,6 +104,23 @@ public void CanBindParameterToTheDefaultForm(bool suppressEnhancedNavigation) DispatchToFormCore(dispatchToForm); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void InputHiddenCanStoreData(bool suppressEnhancedNavigation) + { + var dispatchToForm = new DispatchToForm(this) + { + Url = "forms/default-form-input-hidden", + FormCssSelector = "form", + SuppressEnhancedNavigation = suppressEnhancedNavigation, + }; + DispatchToFormCore(dispatchToForm); + + Browser.Equal("stranger", () => Browser.Exists(By.Id("hidden")).GetDomProperty("value")); + Browser.Equal("Hello stranger!", () => Browser.Exists(By.Id("pass")).Text); + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/DefaultFormInputHidden.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/DefaultFormInputHidden.razor new file mode 100644 index 000000000000..75107fb33e9b --- /dev/null +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/DefaultFormInputHidden.razor @@ -0,0 +1,21 @@ +@page "/forms/default-form-input-hidden" +@page "/reexecution/forms/default-form-input-hidden" +@using Microsoft.AspNetCore.Components.Forms + +

Default form Input Hidden

+ + + + + + +@if (_submitted) +{ +

Hello @Parameter!

+} + +@code { + bool _submitted = false; + + [SupplyParameterFromForm] public string Parameter { get; set; } = "stranger"; +}