0

I have a simple Blazor search form with multiple search parameter fields. Each field has its validation message defined in a class, but I'd like to be able to pass extra text into one of the field's validation messages, as that specific field's name is actually variable based on a value pulled from a database. So I can't just say "First Name is required", because sometimes the field's label doesn't say "First Name", but instead says "Client Name" or "Vehicle Make and Model", and I want the validation message to reflect the current field name.

So, is there a way to pass in dynamic text to the ValidationMesage?

sample code:

<div class="row">
    <label class="col-12 col-sm-4 col-form-label">@FirstFieldLabel:</label>
    <div class="col-12 col-sm-8 ps-2">
        <InputText id="FirstField "
                   class="form-control form-control-sm"
                   @bind-Value="@SearchFields.FirstField " />
        <ValidationMessage For="() => SearchFields.FirstField " />
    </div>
</div>

@code {
    private string? FirstFieldLabel { get; set; }

    private class SearchFields() {
        [StringLength(100, ErrorMessage = "Must be between 3 and 100 characters.")]
        public string? FirstField { get; set; }
    }

    protected override async Task OnInitializedAsync() {
        FirstFieldLabel = _dbLayer.GetFirstFieldLabel();
    }
}

I'd like to insert FirstFieldLabel into the SearchFields() ErrorMessage for FirstField, so that the message instead reads "FirstField must be between 3 and 100 characters.".

4
  • Is your row inside an EditForm and did you include DataAnnotationsValidator? Commented Mar 11, 2024 at 20:26
  • Yes and yes. I just didn't include the whole form - it's a lot of code. Commented Mar 11, 2024 at 22:23
  • 1
    The way I see it, you could try a validation library like Fluent Validation with Blazor or you could create custom attributes according to your need and have them fetch the validation parameters internally and control the validation. See also Writing Custom Validation Commented Mar 11, 2024 at 22:34
  • The <ValidationMessage> component is used to display validation messages related to specific fields. It typically works alongside data annotations to display automatically generated error messages from attributes like [Required] or [StringLength]. This means it directly shows the error messages defined on the model properties and doesn't directly support dynamically changing these messages on the fly. maybe you could try to use customize attribute or reflect to deal it Commented Mar 12, 2024 at 10:08

2 Answers 2

1

You could try make a CustomValidationMessage component which inherit from ValidationMessage and pass any parameter to render as you need.

CustomValidationMessage.razor

@inherits ValidationMessage<TValue>
@typeparam TValue

<div class="text-danger">
    @this.BaseContent
    @newMessage
</div>

@code {
    // BaseContent is the error message from object attribute
    private RenderFragment BaseContent => (builder) => base.BuildRenderTree(builder);

    //create parameter to pass new message
    [Parameter] public string newMessage { get; set; }
}

Then you could use like

<CustomValidationMessage For="() => SearchFields.FirstField " newMessage="@FirstFieldLabel" />
Sign up to request clarification or add additional context in comments.

Comments

0

Your

[StringLength(100, ErrorMessage = "Must be between 3 and 100 characters.")]

is obviously nonesense, and can be fixed by using Fluent Validation or writing a custom validator as per HMZ's comment.

That though doesn't solve the field name problem.

You can do that using a custom ValidationMessage component.

The one below demonstrates the principle. It inherits from ValidationMessage and just does placeholder substitution to format out the message.

The full ValidationMessage code is here.

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Rendering;
using System.Linq.Expressions;

namespace xxxx.Components;

public class MyValidationMessage<TValue> : ValidationMessage<TValue>
{
    [CascadingParameter] private EditContext EditContext { get; set; } = default!;
    [Parameter, EditorRequired] public string? FieldName { get; set; }
    private FieldIdentifier _fieldIdentifier;
    private Expression<Func<TValue>>? _previousFieldAccessor;

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        foreach (var message in EditContext.GetValidationMessages(_fieldIdentifier))
        {
            builder.OpenElement(0, "div");
            builder.AddAttribute(1, "class", "validation-message");
            builder.AddMultipleAttributes(2, AdditionalAttributes);
            builder.AddContent(3, this.FormatMessage(message));
            builder.CloseElement();
        }
    }

    private string FormatMessage(string message)
    {
        if (this.FieldName is null)
            return message;

        return message.Replace("[FieldName]", this.FieldName);
    }

    protected override void OnParametersSet()
    {
        if (this.EditContext is null)
            ArgumentNullException.ThrowIfNull(nameof(this.EditContext));

        if (this.For is null)
            ArgumentNullException.ThrowIfNull(nameof(this.For));

        if (this.For != _previousFieldAccessor)
        {
            _fieldIdentifier = FieldIdentifier.Create(For);
            _previousFieldAccessor = this.For;
        }
    }
}

Demo Page:

@page "/"
@using System.ComponentModel.DataAnnotations

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

<EditForm Model="_model" >
<DataAnnotationsValidator />
<div class="row">
    <label class="col-12 col-sm-4 col-form-label">@FirstFieldLabel:</label>
    <div class="col-12 col-sm-8 ps-2">
        <InputText id="FirstField "
                   class="form-control form-control-sm"
                   @bind-Value="_model.FirstField " />
        <MyValidationMessage For="() => _model.FirstField " FieldName="@this.FirstFieldLabel" />
    </div>
</div>
</EditForm>

@code {
    private string? FirstFieldLabel { get; set; } = "Fred";
    private SearchFields _model = new();

    private class SearchFields() {
        [StringLength(3, ErrorMessage = "[FieldName] must be between 3 and 100 characters.")]
        public string? FirstField { get; set; }
    }
}

3 Comments

This is a fuller version of the answer provided by @QiangFu.
"is obviously nonesense" - Actually, that is the real validation message, and works exactly as expected, so I'm not sure why you think it's nonsense.
[Polite] I assume you've written a custom validator. If so it's not, but unless you say that in the question, it's guesswok.

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.