I'm trying to create a "composite" Blazor text input component.
I'd like to combine
So far, I've created something like this (slightly simplified):
MyInputText.razor
:
<div class="row">
<div class="col-md-3">
<label>@Title</label>
</div>
<div class="col-md-8">
<InputText class="form-control" name="textbox" @bind-Value="Value" @oninput="TextInputEvent" />
<ValidationMessage For="() => Value" />
</div>
<div class="col-md-1">
<label>@CurrentChars / @MaxChars</label>
</div>
</div>
MyInputText.razor.cs
:
public partial class MyTextInput
{
[Parameter]
[EditorRequired]
public string Title { get; set; } = string.Empty;
[Parameter]
[EditorRequired]
public string Value { get; set; } = string.Empty;
[Parameter]
[EditorRequired]
public int MaxChars { get; set; } = 255;
public int CurrentChars { get; set; }
private void TextInputEvent()
{
CurrentChars = Value.Length;
}
}
It seems to almost work - but:
It seems to be always "one step behind" - so if I have four characters, and type in a fifth one - the "CurrentChars" is set to 4. Seems it's looking at the "old" content of Value
- before the char I've just typed is registered
It seems to work only for the first char I type - any subsequent chars don't seem to trigger the TextInputEvent
handler anymore...
Any ideas what I'm missing here?
Other answers cover the need for manual binding in your solution.
The alternative is to inherit directly from InputText
.
The key advantage in doing so is you don't need to reinvent the wheel. You leverage all the built in InputBase
functionality with the EditContext
and validation.
In the example I've added the UpdateOnInput
option as you will probably use it at some point.
@inherits InputText
<div class="row">
<div class="col-md-3">
<label>@Title</label>
</div>
<div class="col-md-8">
<input class="@this.CssClass"
type="text"
value="@this.CurrentValueAsString"
@oninput="this.OnInput"
@onchange="this.OnChange"
@attributes=this.AdditionalAttributes
@ref=this.Element />
<ValidationMessage For="() => Value" />
</div>
<div class="col-md-1">
<label>@CurrentChars / @MaxChars</label>
</div>
</div>
@code {
[Parameter, EditorRequired] public string Title { get; set; } = string.Empty;
[Parameter, EditorRequired] public int MaxChars { get; set; } = 255;
[Parameter] public bool UpdateOnInput { get; set; }
public int CurrentChars { get; set; }
private void OnInput(ChangeEventArgs e)
{
CurrentChars = e?.Value?.ToString()?.Length ?? 0;
if(UpdateOnInput)
CurrentValueAsString = e?.Value?.ToString() ?? null;
}
private void OnChange(ChangeEventArgs e)
{
if (!UpdateOnInput)
CurrentValueAsString = e?.Value?.ToString() ?? null;
}
}
Demo:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<EditForm Model="_model">
<MyInputText class="form-control" @bind-Value="_model.Value" MaxChars="10" Title="My Value" />
<MyInputText class="form-control" @bind-Value="_model.Value2" UpdateOnInput MaxChars="10" Title="My Value" />
</EditForm>
<div class="bg-dark text-white m-2 p-2">
<pre>Value: @_model.Value</pre>
<pre>Value2: @_model.Value2</pre>
</div>
@code{
private Model _model = new();
public class Model
{
public string? Value { get; set; }
public string? Value2 { get; set; }
}
}