Reputation: 666
When using blazor its important to create reusable custom components; that's one of the main points to use Blazor.
I've created a basic Input component that uses Bootstrap for its CSS as shown.
@inherits InputBase<string>
<InputText type="@Type"
class="form-control"
id="@_id"
placeholder="@Placeholder"
@bind-Value="CurrentValue"
disabled="@Disabled"
@attributes="AdditionalAttributes"/>
/* Additional properties below for placeholder, disabled, type, etc. they're just string properties */
I place this inside an EditForm like so...
<EditForm Model="MyModel"
OnValidSubmit="ViewModel.CreateAsync">
<DataAnnotationsValidator />
<PrimaryInput @bind-Value="MyModel.Name"
Placeholder="Name..."/>
</EditForm>
Problem is, this doesn't add the "invalid" class to the item. Both valid and modified appear, but once the input becomes invalid it does not change valid to invalid like it would with a none custom component. This is my model below to show I've added validation to the model.
public class MyModel
{
[StringLength(5)]
[Required]
public string Name { get; set; }
}
Below is the image of what is displayed:
This is the html that is generated by Blazor...
<input type="text" id="aa106d17-46d7-442c-be39-6c947f17186b" placeholder="Name..." aria-describedby="14b1189e-c3c5-4e1f-ae1d-330756147b44" class="form-control valid" aria-invalid="">
...as you can see, the class is still valid but the aria-invalid attribute has been applied. Below is the HTML for a none custom component input being validated...
<input class="modified invalid" aria-invalid="">
...on this one, invalid is applied. Something is preventing the class from switching to invalid on the custom component.
Upvotes: 2
Views: 3005
Reputation: 666
I fixed it with the following code in my custom component....
@inherits InputText
<InputText class="form-control"
Value="@Value"
ValueExpression="ValueExpression"
ValueChanged="OnInputChanged" />
... it's a little strange inheriting InputText with an InputText in there. My code behind for it is like so...
private void OnInputChanged(string value)
{
CurrentValueAsString = value;
EditContext.NotifyFieldChanged(FieldIdentifier);
}
Then in my code I place that component like so....
<TestInput @bind-Value="ViewModel.Menu.Name"/>
It's a little strange work around, but does the job and applies the correct classes when it's valid/invalid.
Upvotes: 1
Reputation: 30485
You are inheriting from the <InputBase>
and placing an InputText
(an InputBase
control) inside it. This doesn't really make sense.
Here's an example that demonstrates how to customize a standard InputText
. It shows how to add custom attributes, one way of handling disabled
, how user entered attributes are added to the input, and this one updates the value on keyboard entry rather than on losing focus.
@namespace Blazor.Starter.Components
@inherits InputText
<input type="text" disabled="@Disabled" value="@this.CurrentValue" @oninput="OnInput" @ref="Element" id="@Id" @attributes="this.AdditionalAttributes" />
@code{
[Parameter] public ElementReference? Element { get; set; }
[Parameter] public string? Id { get; set; }
[Parameter] public bool Disabled { get; set; }
private void OnInput(ChangeEventArgs e)
=> this.CurrentValueAsString = e.Value.ToString();
}
@page "/Test3"
<EditForm EditContext="@_editContext" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<label class="form-label">First Name</label>
<CustomInput class="form-control" @bind-Value="this._formModel.FirstName" Id="mycontrol"></CustomInput>
<label class="form-label">First Name</label>
<CustomInput class="form-control" @bind-Value="this._formModel.FirstName" Id="mydisabledcontrol" Disabled="true"></CustomInput>
<div class=" m-2 p-2">
<button type="submit" class="btn btn-primary">New Person</button>
</div>
<div class="p3-m-3">Value: @_formModel.FirstName</div>
</EditForm>
@code {
private EditContext? _editContext;
private FormModel _formModel;
private void HandleValidSubmit()
{
// handler
}
private class FormModel
{
[StringLength(5)][Required] public string FirstName { get; set; }
}
protected override Task OnInitializedAsync()
{
_formModel = new FormModel();
_editContext = new EditContext(_formModel);
return base.OnInitializedAsync();
}
}
See the two html snippets for when the input is valid/invalid.
<input type="text" id="mycontrol" class="form-control" _bl_759b54c2-54a4-461f-8101-9661c9034c17>
<input type="text" id="mycontrol" class="form-control" _bl_759b54c2-54a4-461f-8101-9661c9034c17 aria-invalid>
Upvotes: -1
Reputation: 3422
You need to trigger the form's EditContext
. There are a few ways to do this - I'll outline two of 'em.
Inside your custom component you can override TryParseValueFromString
and handle the validation there - InputBase<>
has these properties within it. This may not make sense considering your type is a string
already.
Otherwise, you can have your custom component have the EditContext
inject as a cascading parameter and then have an event callback on change to invoke the EditContext
's NotifiedFieldChanged
.
Small example below of this second example:
[CascadingParameter]
private EditContext EditContext { get; set; }
[Parameter]
private Expression<Func<string>> ValueExpression { get; set; }
private FieldIdentifier _field;
private void OnInitialized()
{
_field = FieldIdentifier.Create(ValueExpression);
}
private void OnInputChanged(ChangeEventArgs args)
{
EditContext.NotifyFieldChanged(_fieldIdentifier);
}
Upvotes: 1