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

View File

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

View File

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

View File

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