I'm working on my version of an auto complete component in a Blazor WASM (Standalone) app targeting .NET 9.
I'm able to bind an input
to a string
variable and take action as user types in his/her search keyword using @bind:after
using the following code and it works fine but when I try to move the UI of my auto complete into a component, binding the input
to my variable doesn't seem to work. The bind:after
part continues to work fine and I hit my method after each keystroke.
Here's the code that works fine which I then move into a component:
...
<input @bind="UserInputText" @bind:event="oninput" @bind:after="OnUserInputChanged" />
...
@code {
private string? UserInputText = "";
private void OnUserInputChanged()
{
// Process user input and make suggestions
}
}
I then take the UI part and place it into a new component I'm creating to encapsulate auto-complete into its own component.
Here's the parent code:
...
<AutoComplete UserInputText="UserInput" UserInputTextChangedHandler="OnUserInputChanged" />
...
@code {
private string? UserInput = "";
private void OnUserInputChanged()
{
// Process user input and make suggestions
}
}
And here's the code inside the AutoComplete
component:
@typeparam TItem
<div>
<input @bind="UserInputText" @bind:event="oninput" @bind:after="UserInputTextChangedHandler" />
</div>
@code {
[Parameter]
public string? UserInputText { get; set; }
[Parameter]
public Action UserInputTextChangedHandler { get; set; }
[Parameter]
public List<TItem> Suggestions { get; set; }
}
BTW, I tried using EventCallback
instead of Action
for passing OnUserInputChanged
to the auto complete component but then I get an error that states:
Argument 1: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback' to 'System.Action'
Please also keep in mind that wiring the OnUserInputChanged
actually continues to work fine even after I place that logic into the new component. The part that no longer works is binding UserInputText
to the input
inside the component.
Where am I making a mistake?
I'm working on my version of an auto complete component in a Blazor WASM (Standalone) app targeting .NET 9.
I'm able to bind an input
to a string
variable and take action as user types in his/her search keyword using @bind:after
using the following code and it works fine but when I try to move the UI of my auto complete into a component, binding the input
to my variable doesn't seem to work. The bind:after
part continues to work fine and I hit my method after each keystroke.
Here's the code that works fine which I then move into a component:
...
<input @bind="UserInputText" @bind:event="oninput" @bind:after="OnUserInputChanged" />
...
@code {
private string? UserInputText = "";
private void OnUserInputChanged()
{
// Process user input and make suggestions
}
}
I then take the UI part and place it into a new component I'm creating to encapsulate auto-complete into its own component.
Here's the parent code:
...
<AutoComplete UserInputText="UserInput" UserInputTextChangedHandler="OnUserInputChanged" />
...
@code {
private string? UserInput = "";
private void OnUserInputChanged()
{
// Process user input and make suggestions
}
}
And here's the code inside the AutoComplete
component:
@typeparam TItem
<div>
<input @bind="UserInputText" @bind:event="oninput" @bind:after="UserInputTextChangedHandler" />
</div>
@code {
[Parameter]
public string? UserInputText { get; set; }
[Parameter]
public Action UserInputTextChangedHandler { get; set; }
[Parameter]
public List<TItem> Suggestions { get; set; }
}
BTW, I tried using EventCallback
instead of Action
for passing OnUserInputChanged
to the auto complete component but then I get an error that states:
Argument 1: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback' to 'System.Action'
Please also keep in mind that wiring the OnUserInputChanged
actually continues to work fine even after I place that logic into the new component. The part that no longer works is binding UserInputText
to the input
inside the component.
Where am I making a mistake?
Share Improve this question edited Mar 30 at 21:46 Sam asked Mar 30 at 21:41 SamSam 30.6k76 gold badges252 silver badges464 bronze badges1 Answer
Reset to default 1You should not bind a component Parameter to an input - use a local instead.
The reason is that Blazor keeps a copy of the Component state and will set your Parameter from that state when the Component is going to be rendered. If you bind to that Parameter inside the Component, you have a potential race condition.
So, one possible approach for your code might be to bind the input to a local property which has a getter that returns the Parameter value a setter that updates a local field. You could then pass the local field (i.e. the current value) to your action UserInputTextChangedHandler and allow the parent to update it's state from there.
AutoComplete.razor
@typeparam TItem
<div>
<input list=suggestions @bind=BindableInputText @bind:event=oninput @bind:after=@(()=>UserInputTextChangedHandler(_currentInputText)) />
<datalist id=suggestions>
@foreach (var item in Suggestions ?? [])
{
<option>@item</option>
}
</datalist>
</div>
@code {
private string? _currentInputText;
private string? BindableInputText { get => UserInputText; set => _currentInputText = value; }
[Parameter]
public string? UserInputText { get; set; }
[Parameter]
public required Action<string?> UserInputTextChangedHandler { get; set; }
[Parameter]
public List<TItem>? Suggestions { get; set; } = [];
}
Parent
<AutoComplete UserInputText=@UserInput UserInputTextChangedHandler="OnUserInputChanged" Suggestions=@Suggestions />
<div>Text entered: @UserInput</div>
@code {
private string? UserInput = "";
private List<string> Suggestions = [];
private void OnUserInputChanged(string? UserInputText)
{
UserInput = UserInputText;
Suggestions = Enumerable.Range(1,5).Select(i=> $"{UserInputText} {i}").ToList();
StateHasChanged();
}
}