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:
Steve Sanderson 2019-02-25 21:49:28 +00:00 committed by GitHub
parent 3c558b27b4
commit 61098b6680
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 3 deletions

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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"))

View File

@ -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" />