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>
|
/// <summary>
|
||||||
/// Gets or sets a callback that updates the bound value.
|
/// Gets or sets a callback that updates the bound value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] Action<T> ValueChanged { get; set; }
|
[Parameter] EventCallback<T> ValueChanged { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets an expression that identifies the bound value.
|
/// Gets or sets an expression that identifies the bound value.
|
||||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components.Forms
|
||||||
if (hasChanged)
|
if (hasChanged)
|
||||||
{
|
{
|
||||||
Value = value;
|
Value = value;
|
||||||
ValueChanged?.Invoke(value);
|
_ = ValueChanged.InvokeAsync(value);
|
||||||
EditContext.NotifyFieldChanged(FieldIdentifier);
|
EditContext.NotifyFieldChanged(FieldIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,8 @@ namespace Microsoft.AspNetCore.Components.Forms
|
||||||
{
|
{
|
||||||
childBuilder.OpenComponent<TComponent>(0);
|
childBuilder.OpenComponent<TComponent>(0);
|
||||||
childBuilder.AddAttribute(0, "Value", Value);
|
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(2, "ValueExpression", ValueExpression);
|
||||||
childBuilder.AddAttribute(3, nameof(Id), Id);
|
childBuilder.AddAttribute(3, nameof(Id), Id);
|
||||||
childBuilder.AddAttribute(4, nameof(Class), Class);
|
childBuilder.AddAttribute(4, nameof(Class), Class);
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,27 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
WaitAssert.Empty(emailMessagesAccessor);
|
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)
|
private Func<string[]> CreateValidationMessagesAccessor(IWebElement appElement)
|
||||||
{
|
{
|
||||||
return () => appElement.FindElements(By.ClassName("validation-message"))
|
return () => appElement.FindElements(By.ClassName("validation-message"))
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
<option value="@TicketClass.Premium">Premium class</option>
|
<option value="@TicketClass.Premium">Premium class</option>
|
||||||
<option value="@TicketClass.First">First class</option>
|
<option value="@TicketClass.First">First class</option>
|
||||||
</InputSelect>
|
</InputSelect>
|
||||||
|
<span id="selected-ticket-class">@person.TicketClass</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="accepts-terms">
|
<p class="accepts-terms">
|
||||||
Accepts terms: <InputCheckbox bind-Value="@person.AcceptsTerms" />
|
Accepts terms: <InputCheckbox bind-Value="@person.AcceptsTerms" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue