Recently, in Blazor 7, a feature has been added to make it easier to bind and call the method based on changes in the bound expression.
In .NET 7, you can now easily run async logic after a binding event has completed using the new @bind:after modifier:
<input @bind="searchText" @bind:after="PerformSearch" />
@code {
string searchText = "";
async Task PerformSearch()
{
// Do something async with searchText
}
}
In this example, the PerformSearch async method runs automatically after any changes to the search text are detected.
Another method has been added too. The @bind:get and @bind:set modifiers are always used together. The @bind:get modifier specifies the value to bind to, and the @bind:set modifier specifies a callback that's called when the value changes.
The questions are:
What is the difference between @bind:after="PerformSearch"
and @bind:set="PerformSearch"
? Both of these seem to call the PerformSearch
after the searchText
is changed.
Where is the use of each?
What is the difference between @bind:after="PerformSearch" and @bind:set="PerformSearch"?
You should only use @bind:after="PerformSearch"
with @bind="searchText"
,in which case the bind will set the value of searchText
, so you shouldn't also try and set it in PerformSearch
.
If you use @bind:set="PerformSearch"
then you must set the value of searchText
in PerformSearch
, and use @bind:get="searchText"
.
Where is the use of each?
The MS Docs article I think gives a good guide. It all depends on your knowledge level on components.
It's important to understand two points:
Also note:
There's been a lot of MS Blazor Team activity on this subject since 7.0 was released. See https://github.com/dotnet/aspnetcore/issues/44957 for details on problems with the way the Razor compiler treats the @bind directives.
There are some updates to the MS Docs on the subject - https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#blazor-custom-elements.
Here's my demo page for this answer.
@page "/"
<PageTitle>Index</PageTitle>
<input class="form-control mb-3" type="text" @bind:get="this.Value" @bind:set="ValueSetter" @bind:event="oninput" />
<input class="form-control mb-3" type="text" @bind:get="this.Value" @bind:after="ValueSetter" />
<input class="form-control mb-3" type="text" @bind="this.Value" @bind:after="DoSearch" @bind:event="oninput"/>
<div class="alert alert-info m-2 p-2">
@Value
</div>
<div class="alert alert-primary m-2 p-2">
@message
</div>
@code {
private string? Value;
private string message = "Not Set";
private async Task DoSearch()
{
await Task.Delay(1000);
message= $"Set at {DateTime.Now.ToLongTimeString()}";
}
private void ValueSetter(string __value)
=> this.Value = __value;
private Task SearchSetter(string __value)
{
this.searchText = __value;
return DoSearch();
}
}
Let's look at the actual C# code the Razor compiler builds.
This is the code snippet when just using bind:set=this.ValueSetter
:
__builder.AddAttribute(8, "oninput", EventCallback.Factory.CreateBinder(
this,
CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: this.ValueSetter,
value: this.Value
),
this.Value));
This simply calls the setter delegate assigned to set.
This is the code snippet when using :bind=this.Value
and @bind:after=DoSearch
:
__builder.AddAttribute(14, "oninput", EventCallback.Factory.CreateBinder(
this, CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: __value => {
this.Value = __value;
return RuntimeHelpers.InvokeAsynchronousDelegate(callback: DoSearch);
},
value: this.Value),
this.Value));
It's a little more complicated. The compiler builds the equivalent to this:
Task AnonymousMethod(string __value)
{
this.Value = __value;
return DoSearch()
}
Depending on your development environment, you will get errors with certain combinations. Some of which at the moment appear to be misleading or totally wrong. They will be fixed shortly.
Quote from Dan Roth: Hi folks. The VS fix for this just missed the window for 17.4.4 but should be addressed in the next VS patch update in February. We apologize for the wait and thank you for your patience!
In Visual Studio.
This is a correct error:
<InputText class="form-control" @bind-Value:get="this.searchText" @bind-Value:set="this.SetSearchText" @bind-Value:after="DoSearch" />
Severity Code Description Project File Line Suppression State
Error (active) RZ10019 Attribute 'bind-Value:after' can not be used with 'bind-Value:set'. Invoke the code in 'bind-Value:after' inside 'bind-Value:set' instead.
While this is bull!
<input class="form-control mb-3" type="text" @bind:get="this.Value" @bind:set="ValueSetter" @bind:event="oninput" />
And while it gives this error compiles and runs!
Severity Code Description Project File Line Suppression State
Error (active) CS1503 Argument 3: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<string>' to 'System.Action<string?>'
And this line:
<input class="form-control mb-3" type="text" @bind:get="this.Value" @bind:after="ValueSetter" />
Compiles but is obviously also total bull.
__builder.AddMarkupContent(9, "\r\n\r\n<input class=\"form-control mb-3\" type=\"text\" @bind:get=\"this.Value\" @bind:after=\"ValueSetter\">\r\n\r\n");