-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Design proposal: Bind get/set/after modifiers #39837
Comments
Perhaps |
It'll also make it easier to understand that it's a handler, but will break the "get/set" combination, which is also easy to remember. Choosing between easy to remember and intuitive / help me understand what's going on here - I'd chose the latter. So, |
I somewhat dislike |
Does |
@SteveSandersonMS overall a very solid proposal, I like the direction this goes in an the fact that people can reuse our custom bind "reconciliation" logic. |
I agree. I don't really see
I have to actually implement it to get to 100% solid certainty, but from what I anticipate, no. My intent is that the |
I love this proposal. Not kidding, huge improvement. Typical of @SteveSandersonMS . |
Will I be able to use :after (and others) with components? <MyComponent @bind-SomeValue="_value" @bind-SomeValue:after="HandleValue" /> |
Yes |
Really like this proposal. |
:after is nice! |
Nice! How about using bind:before instead of bind:set? I did find it a bit missleading |
@vgb1993 We don't think that will be such a common requirement, and for people who do need that, it's important that they use |
Summary
Provides ways of customizing how Blazor's
@bind
reads and writes a value, solving several common problems at once.Motivation and goals
After thinking through the "multiple event handlers" proposal (#39815), I've started to think the real problem we should solve is quite different. That is:
@bind
(see below).@bind
today:A. No safe way to run async logic after a binding updates (#22394). This is a basic scenario even beginners will hit early.
B. The magic consistency mechanism built into
@bind
(essential for Blazor Server apps) can't be used if you're implementing avalue
/onchange
pair manually (#17281 #38520). This is a potential severe gotcha you may not notice until in production.C. When building a component with a
Value
/ValueChanged
pair, there are two ways to do it, but neither are safe:@value
/@onchange
pair manually, you lose consistency guarantees (can lose keystrokes on Server)@bind
to a property with aget
/set
pair, you have to discard theTask
returned byValueChanged.InvokeAsync
In scope
Value
/ValueChanged
pairThese two directly address A and C above. If these two are done, I think B is solved implicitly because there's no longer any reason to want to do a manual
value
/onchange
pair. If the behavior you want is conceptually a two-way binding, then@bind
would now always have as much power as you need.Also, I'm keen to do this in an idiomatically Blazory way. For example, there should not be some kind of new
BindableValue
that you have to instantiate at runtime. Instead, typical components should be built only with basic C# types and patterns (e.g.,string
andFunc<string, Task>
).Out of scope
@bind
safely (that is, in a way that won't drop keystrokes in async update cases like Blazor Server), I don't think we can realistically stop people from using extra powers to do something misguided.Proposed solution
There are really two distinct cases to deal with. I've considered extensively designs that would solve both using only a single concept, but didn't feel that any of them led to enough elegance or convenience. So in the end I'm proposing two different but closely-related new mechanisms.
bind:after
To handle the "run async logic after binding", consider a
@bind:after
directive attribute:I really like
@bind:after
because it's so basic. Any beginner can understand this. Even just seeing its name in autocompletion is probably enough to work out how to use it. This solves problem A in a super direct way.It's also very good for guiding people not to break the safety mechanism for not losing keystrokes in Blazor Server, since your custom logic doesn't run until we've already assigned the new value to
searchText
synchronously - all existing guarantees remain. Presumably we'd invokePerformSearch
before the initial re-render, and as usual, have it trigger a further re-render asynchronously. This would happen automatically if the Razor compiler generates an event handler that does the assignment and then returns the task given byEventCallback.Factory.Create(yourBindAfterExpression).InvokeAsync()
.bind:get and bind:set
This one's less obvious, but for components that take a
Value
/ValueChanged
pair, you'd be able to take the scary code you have to write today:... and simplify it to this:
With benefits:
value=
sideValueChanged
)The behavior of course is that you're controlling parts of the code that
@bind
generates on both the reading and writing sides. The difference between@bind:set
and@bind:after
is that@bind:after
does the value writing for you before calling your code, whereas@bind:set
just calls your code (and passes the new value, having gone through@bind
's built-in parsing logic).Questions you may have:
@bind:get
+@bind:set
and not just@bind
+@bind:set
?<input @bind="@val" @bind:set="@MyMethod" />
often, it creates confusion:@bind:set
is what makes it a two-way binding, and that you could make it one-way by removing that. Whereas in fact that would be wrong (you'd still have a two-way binding, just one that behaves differently now).<input value="@val" @bind:set="@MyMethod />
, and it almost is, but not quite because the formatting logic would differ. Much better not to create the ambiguity and have one correct solution.@bind:get
and@bind:set
must always be used as a pair - you can't just have one of them and not the other (nor can you have them with@bind
). So none of the weird cases will arise.@bind:set
to achieve (in effect)@bind:after
, and hence we don't need@bind:after
?@bind:set
to a method that writes to your field and then runs your async logic. However, this is far less obvious for newcomers, and is less convenient in common cases. And it invites mistakes: if you do something async before setting the field, the UI will temporarily revert and generally behave badly. So it's valuable to have@bind:after
for convenience and to guide correct usage. We can regard@bind:get
/@bind:set
as a more advanced case mainly for people implementing bindable components, as it gives them a really clean and safe solution, and such developers are advanced enough to understand that they just shouldn't do async work before callingValueChanged
.<input @bind:get="@value" @bind:set="@MyMethod" @bind:after="@DoStuff" />
?MyMethod
before callingDoStuff
, since "after" feels like it means "after all the work involved in calling set". It's an edge case but I can't think of any problems this will cause nor any major increase in implementation cost.@bind
modifiers like@bind:event
and@bind:format
work with this?value
/onchange
pairs.Risks / unknowns
Depends on the exact design chosen.
In the above design, it's increasing the conceptual surface area of
@bind
a bit and introducing new rules about which combinations of modifiers can be used together. I don't think this is a killer problem because the compiler can enforce all this cleanly. However it is potentially more for people to learn. Mitigation: only tell most people about@bind:after
, as that's the most widely needed and is super easy to understand.Examples
See above
The text was updated successfully, but these errors were encountered: