diff --git a/src/Components/Components/src/Forms/InputComponents/InputBase.cs b/src/Components/Components/src/Forms/InputComponents/InputBase.cs index a1c61da5bd..4c7f525afd 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputBase.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputBase.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Components.Forms /// /// Gets or sets a callback that updates the bound value. /// - [Parameter] Action ValueChanged { get; set; } + [Parameter] EventCallback ValueChanged { get; set; } /// /// Gets or sets an expression that identifies the bound value. @@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components.Forms if (hasChanged) { Value = value; - ValueChanged?.Invoke(value); + _ = ValueChanged.InvokeAsync(value); EditContext.NotifyFieldChanged(FieldIdentifier); } } diff --git a/src/Components/Components/test/Forms/InputBaseTest.cs b/src/Components/Components/test/Forms/InputBaseTest.cs index cf0e33a0b6..a66078798c 100644 --- a/src/Components/Components/test/Forms/InputBaseTest.cs +++ b/src/Components/Components/test/Forms/InputBaseTest.cs @@ -459,7 +459,8 @@ namespace Microsoft.AspNetCore.Components.Forms { childBuilder.OpenComponent(0); childBuilder.AddAttribute(0, "Value", Value); - childBuilder.AddAttribute(1, "ValueChanged", ValueChanged); + childBuilder.AddAttribute(1, "ValueChanged", + EventCallback.Factory.Create(this, ValueChanged)); childBuilder.AddAttribute(2, "ValueExpression", ValueExpression); childBuilder.AddAttribute(3, nameof(Id), Id); childBuilder.AddAttribute(4, nameof(Class), Class); diff --git a/src/Components/test/E2ETest/Tests/FormsTest.cs b/src/Components/test/E2ETest/Tests/FormsTest.cs index f7046bc256..1ebf4c3e09 100644 --- a/src/Components/test/E2ETest/Tests/FormsTest.cs +++ b/src/Components/test/E2ETest/Tests/FormsTest.cs @@ -316,6 +316,27 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests WaitAssert.Empty(emailMessagesAccessor); } + [Fact] + public void InputComponentsCauseContainerToRerenderOnChange() + { + var appElement = MountTestComponent(); + var ticketClassInput = new SelectElement(appElement.FindElement(By.ClassName("ticket-class")).FindElement(By.TagName("select"))); + var selectedTicketClassDisplay = appElement.FindElement(By.Id("selected-ticket-class")); + var messagesAccessor = CreateValidationMessagesAccessor(appElement); + + // Shows initial state + WaitAssert.Equal("Economy", () => selectedTicketClassDisplay.Text); + + // Refreshes on edit + ticketClassInput.SelectByValue("Premium"); + WaitAssert.Equal("Premium", () => selectedTicketClassDisplay.Text); + + // Leaves previous value unchanged if new entry is unparseable + ticketClassInput.SelectByText("(select)"); + WaitAssert.Equal(new[] { "The TicketClass field is not valid." }, messagesAccessor); + WaitAssert.Equal("Premium", () => selectedTicketClassDisplay.Text); + } + private Func CreateValidationMessagesAccessor(IWebElement appElement) { return () => appElement.FindElements(By.ClassName("validation-message")) diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.cshtml b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.cshtml index 6941594fc1..bfa01565da 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.cshtml +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.cshtml @@ -34,6 +34,7 @@ + @person.TicketClass

Accepts terms: