4

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?

Arani
  • 891
  • 7
  • 18

2 Answers2

5

What is the difference between @bind:after="PerformSearch" and @bind:set="PerformSearch"?

  1. 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.

  2. 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:

  1. This is Razor syntax, not C#.
  2. It's just syntactic sugar: high level functionality, shorthand Razor directives to encapsulate existing functionality.

Also note:

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()
}

A Note on Development Environment Errors

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

enter image description here

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
2

Why is it @bind:get+@bind:set and not just @bind+@bind:set?

  • Because if you see <input @bind="@val" @bind:set="@MyMethod" /> often, it creates confusion:

  • It looks as if the @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).

  • It looks as if it would be equivalent to write <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.

  • We can avoid the above problems by having a compiler rule that @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.

Couldn't you use @bind:set to achieve (in effect) @bind:after, and hence we don't need @bind:after?

  • Theoretically yes. You could @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 calling ValueChanged.

Can you use all three at once, e.g., <input @bind:get="@value" @bind:set="@MyMethod" @bind:after="@DoStuff" />?

  • Sure, why not? I think that the generated logic should await MyMethod before calling DoStuff, 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.

Do other @bind modifiers like @bind:event and @bind:format work with this?

  • Yes, and that's partly why it's a big improvement over manual value/onchange pairs.

you can refer this link get more idea https://github.com/dotnet/aspnetcore/issues/39837

H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    I wish you would add this part of the text inside the link to your answer: `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).` – Arani Jan 29 '23 at 12:08
  • yes,you are correct – Madusha Prasad Jan 29 '23 at 16:51
  • 1
    You can't use bind:after together with bind:set, at least not in net7. Doing so will throw this error: Attribute 'bind:after' can not be used with 'bind:set'. Invoke the code in 'bind:after' inside 'bind:set' instead. – Matěj Štágl Jun 22 '23 at 17:11