Make InputBase use EventCallback for ValueChanged (#7915)
* Make InputBase use EventCallback for ValueChanged ... so that the host component gets re-rendered automatically after each value change (like when binding to DOM elements). * Improve E2E test code ... so that the host component gets re-rendered automatically after each value change (like when binding to DOM elements).
This commit is contained in:
parent
3c558b27b4
commit
61098b6680
|
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Components.Forms
|
|||
/// <summary>
|
||||
/// Gets or sets a callback that updates the bound value.
|
||||
/// </summary>
|
||||
[Parameter] Action<T> ValueChanged { get; set; }
|
||||
[Parameter] EventCallback<T> ValueChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,7 +459,8 @@ namespace Microsoft.AspNetCore.Components.Forms
|
|||
{
|
||||
childBuilder.OpenComponent<TComponent>(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);
|
||||
|
|
|
|||
|
|
@ -316,6 +316,27 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
WaitAssert.Empty(emailMessagesAccessor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InputComponentsCauseContainerToRerenderOnChange()
|
||||
{
|
||||
var appElement = MountTestComponent<TypicalValidationComponent>();
|
||||
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<string[]> CreateValidationMessagesAccessor(IWebElement appElement)
|
||||
{
|
||||
return () => appElement.FindElements(By.ClassName("validation-message"))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
<option value="@TicketClass.Premium">Premium class</option>
|
||||
<option value="@TicketClass.First">First class</option>
|
||||
</InputSelect>
|
||||
<span id="selected-ticket-class">@person.TicketClass</span>
|
||||
</p>
|
||||
<p class="accepts-terms">
|
||||
Accepts terms: <InputCheckbox bind-Value="@person.AcceptsTerms" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue