0
votes

I am fairly new to Blazor. I am trying to render custom component inside EditForm by referring this page.

But when I submit the form without value, border color not changed to red. Also when submitting the form after clearing the entered value, border not changed to red. It stays in green.

Screenshot:

image

Code Snippet

NativeTextboxComponent.razor

<InputText @bind-Value="@Value" class="form-control" />

@code {
 public string _Value;
    [Parameter]
    public string Value
    {
        get
        {
            return _Value;
        }
        set
        {
            if (_Value != value)
            {
                ValueChanged.InvokeAsync(value);
            }
            _Value = value;
        }
    }
    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

}
}

Index.razor

<EditForm Model="@model" OnInvalidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <NativeTextBoxComponent @bind-Value="@model.NativeValue"></NativeTextBoxComponent>
    <ValidationMessage For="() => model.NativeValue" />
    <button type="submit">Submit</button>
</EditForm>

public class Countries
    {
        [Required]
        public string Values { get; set; }
        [Required]
        public string LastName { get; set; }
        [Required]
        public string NativeValue { get; set; }
    }
    public Countries model = new Countries();

FieldCssClass is returned modified valid for this case.

image

Any help would be greatly appreciated.

3
I'm not sure why this problem occurs, but the preferred solution for such a scenario is to inherit from InputBase<T> rather than wrapping an InputText - Akinzekeel

3 Answers

0
votes

To understand why the FieldCssClass returns valid and the ValidationMessage returns invalid, it is important to observe your component NativeTextboxComponent does to the validation process.

In your example, if you would change your NativeTextBoxComponent to InputText, it will work properly. That is because the @bind-value set the ValueExpression of InputBase. This property is not a simple string but an expression. This expression is used to get the Attributes from your model, like Required.

If you using your <NativeTextBoxComponent @bind-Value="@model.NativeValue"></NativeTextBoxComponent> a simple string is bound to the component. It has lost its connection to its attributes. The two-way binding still works. If your component alters the string, it is written back to the object. That is the reason your <ValidationMessage For="() => model.NativeValue" /> returns an error, if there is no string. But again, from the perspective of your nested <InputText @bind-Value="@Value" class="form-control" /> it is just a string.

To resolve your issue, your component can inherit from InputBase, and you can create your custom layout.

@using System.Diagnostics.CodeAnalysis;

@inherits InputBase<string>

<input @attributes="AdditionalAttributes" class="@CssClass" @bind-value="CurrentValue" />

@code {

    protected override bool TryParseValueFromString(string? value, out string? result, [NotNullWhen(false)] out string? validationErrorMessage)
    {
        result = value;
        validationErrorMessage = null;
        return true;
    }
}
0
votes

I think you are missing the Model where you put what field is required. Here is an example from the docs here

using System.ComponentModel.DataAnnotations;

public class ExampleModel
{
    [Required]
    [StringLength(10, ErrorMessage = "Name is too long.")]
    public string Name { get; set; }
}
0
votes

The ValueExpression is defined as parameter, so the parent component model not updated to child component expression, so the model value not updated in FieldIdentifier cssClass. When render the custom component inside the editform, call the invokeAsync and value expression directly(no need to use two-bind(@bind-Value)).

Index.razor

<EditForm Model="@model">
      <DataAnnotationsValidator />
      <h5>TextBox : @model.FirstName</h5>
    <NativeTextBoxComponent @bind-Value="@model.FirstName" InputHandler="@inputHandler"> </NativeTextBoxComponent >
    <ValidationMessage For="() => model.FirstName" />
   <SfButton CssClass="e-small e-info" Type="submit" Content="Submit"></SfButton>
</EditForm>
@code {
public class Countries
{
    [Required]
    public string FirstName { get; set; }
}
public Countries model = new Countries();

private void inputHandler(ChangeEventArgs args)
{
    model.FirstName = (string)args.Value;
}

}

NativeTextboxComponent.razor

<InputText Value="@Value" ValueChanged="ValueChanged" @oninput="@InputHandler" ValueExpression="@ValueExpression"></InputText>
@code
{ 
private string _value { get; set; }
[Parameter]
public string ID { get; set; }
[Parameter]
public Expression<Func<string>> ValueExpression { get; set; }
[Parameter]
public string Value
{
    get => _value;
    set
    {
        if (!EqualityComparer<string>.Default.Equals(value, _value))
        {
            _value = value;
            ValueChanged.InvokeAsync(value);
        }
    }
}
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
[Parameter]
public EventCallback<ChangeEventArgs> InputHandler { get; set; }

}