[Blazor] `Input*` - allow correct async handling of `ValueChanged` callbacks (incl. `@bind-Value:after`)
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
## Summary
In the current `InputBase` design, the binding to underlying `input` element is implemented by synchronous setter (`CurrentValue_set`). The asynchronous `ValueChanged` callback is called from the synchronous setter as "fire and forget" task (line 78):
https://github.com/dotnet/aspnetcore/blob/905557eaca32f5ce9fd17a25e5187ceb7a75cf1c/src/Components/Web/src/Forms/InputBase.cs#L69-L82
Although there already have been less frequent scenarios where this turned out to be an issue (e.g. manual binding using `Value`, `ValueChanged` and `ValueExpression` parameters), starting with `@bind-Value:after` this will become much bigger pain. Subscribtion to the `ValueChanged` callback will become much easier and I expect the users will start using is much more (imagine continuous saving of values when filling the form).
I would like to start discussion on this topic, try to find a new design for `InputBase` and derived components and allow proper asynchronous handling of the `ValueChanged` callback (incl. `@bind-Value:after` variant).
## Motivation and goals
Consider this sample code. The exception is "lost", neither `blazor-error-ui`, nor `ErrorBoundary` or browser/server-console will capture the exception:
```razor
@page "/"
@**@
@code {
string model = String.Empty;
private Task DoSomethingAfterValueChanged()
{
Console.WriteLine("This executes.");
throw new InvalidOperationException("[1] This exception is lost in async-over-sync call from InputBase.CurrentValue_set.");
// exception not logged in Console
// exception not caught by ErrorBoundary
// exception not caught by Blazor global error UI (the yellow strip of death :-D)
}
}
```
(In opposite, the plain `input` HTML element will behave correctly.)
## Risks
It is obvious, that the solution will cause major breaking changes in current `Input*` components as their `protected` API for inherited components would have to change significantly (the `CurrentValue` and `CurrentValueAsString` properties will have to be replaced with some asynchronous subsitutes). With such major impact, consider creating of new set of `NewInput*` components and keep the old ones as they are.
## Examples
Give brief examples of possible developer experiences (e.g., code they would write).
Don't be deeply concerned with how it would be implemented yet. Your examples could even be from other technology stacks.
# Detailed design
I would like to discuss the issue first with major stakeholders (e.g. @SteveSandersonMS, @javiercn, ...) and based on the approval that "this is something we want to solve" we can elaborate more on the detailed design. It is obvious that we have to handle the `onchange` callback from the `input` element in some asynchronous method rather than synchronous property setter (line 38).
https://github.com/dotnet/aspnetcore/blob/905557eaca32f5ce9fd17a25e5187ceb7a75cf1c/src/Components/Web/src/Forms/InputText.cs#L32-L41
Contributor guide
Research direction
Start by reading src/Components/Web/src/Forms/InputBase.cs and InputText.cs at the linked locations, then reproduce the Razor sample with @bind-Value:after and compare it with the plain input behavior. The issue is currently asking for stakeholder agreement and a new design, so done would require an agreed approach, documented developer experience, and follow-up implementation and tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- frontend, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100