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: