2

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:

enter image description here

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.

3 Answers 3

1

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);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Would you mind going into a bit more detail? Not entirely sure what I'm doing, or is there some documentation where this is described? I'm trying your second example and aren't sure entirely what to do. <input @bind-value="@Value" class="form-control" @onchange="OnInputChanged" /> Heres my code then I also have the code example you posted in your answer but I'm not sure how its all supposed to look.
1

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.

Comments

-1

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();
    }
}
Aria Changes

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>

8 Comments

I appreciate the time you put into your answer but this doesn't solve the issue I'm having where the class "invalid" is not being added even though the input value is invalid.
If you setup and run my example you will see aria-invalid appear and disappear as firstname is/isn't valid.
aria-invalid is added to mine too whether it's valid or invalid, in my question I'm saying how the class doesn't change to reflect the invalid/valid state of the input and that's what I need. So that I can apply styles based on the valid/invalid state of the input.
InputBase adds/removes "aria-invalid" to/from the AdditionalAttributes property based on validation state of the control. In my code above it does it correctly, only adding it if the field is emptied or contains greater than 5 characters. Are you saying you want to apply custom styling based on the validation state of the control?
Yes on a standard InputText as shown in my code above has the class valid or invalid applied based on the validation of the model. Blazor has CSS styling for this by default in the app.css file. My question is asking why arent the invalid and valid classes changing properly based on the validation state in my custom component.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.