I've discovered that my Blazor design for this particular context is unusual, and I'm having trouble figuring out how to address it.
This is my data class models:
Data class model holder of database-fetched data
[Keyless]
public class Specs
{
[Column("id")]
public string? ID { get; set; }
[Column("name")]
public string? Name { get; set; }
[Column("value")]
public string? Value { get; set; }
[Column("rank")]
public int Rank { get; set; }
}
Data class model holder of user-modified data
public class SpecsEntryHolder(string? id, string? n, string? val, int rank)
{
public string? ID { get; set; } = id;
public string? Name { get; set; } = n;
public string? Value { get; set; } = val;
public int Rank { get; set; } = rank;
}
Redefine those data class models as a List
public List<Specs> SpecsList { get; set; } = [];
public List<SpecsEntryHolder> SpecsEntryHolderList { get; set; } = [];
I cloned the data through this approach:
// SpecsList has already been fed with the database records.
SpecsEntryHolderList = SpecsList.Select(v => new SpecsEntryHolder(v.ID, v.Name, v.Value, v.Rank)).ToList();
In the Razor UI page:
<EditForm EditContext="editContext" OnValidSubmit="UpdateSomething" FormName="edit" Enhance>
<DataAnnotationsValidator />
@* other fields here... *@
@foreach (var specs in SpecsEntryHolderList)
{
<div class="col-12 col-sm-6 col-lg-3 mb-3 d-flex flex-column align-items-start">
<label for="someText" class="form-label text-nowrap fw-bold">@($"{specs.Name}")</label>
<InputText id="@($"{specs.ID}")"
@bind-Value="specs.Value"
@onblur="FormatOnBlurSpecs"
class="form-control form-control-sm" />
<ValidationMessage
For="() => specs.Value"
class="text-danger" />
</div>
}
</EditForm>
Note: I have an already existing EditForm setup with <DataAnnotationsValidator /> that has other fields that are separated from this context. I excluded them because they're not part of this case. This note is to mention that I have existing validation for another field, and this should be in a single form because it falls in a single EditContext.
Here's what it looks like:
I was hoping this would work, but it doesn’t:
Version 1
private void FormatOnBlurSpecs(FocusEventArgs e)
{
foreach (var spec in SpecsEntryHolderList)
{
var fieldIdentifier = editContext.Field(nameof(spec.Value));
if (string.IsNullOrWhiteSpace(spec.Value))
{
messageStore!.Add(fieldIdentifier, $"{spec.Name} is required.");
editContext!.NotifyValidationStateChanged();
}
messageStore?.Clear(fieldIdentifier);
editContext!.NotifyValidationStateChanged();
}
}
Version 2
private void FormatOnBlurSpecs(FocusEventArgs e)
{
foreach (var spec in SpecsEntryHolderList)
{
var fieldIdentifier = new FieldIdentifier(spec, nameof(spec.Value));
if (string.IsNullOrWhiteSpace(spec.Value))
{
messageStore!.Add(fieldIdentifier, $"{spec.Name} is required.");
editContext!.NotifyValidationStateChanged();
}
messageStore?.Clear(fieldIdentifier);
editContext!.NotifyValidationStateChanged();
}
}
Version 3 (For testing)
This testing procedure is to verify that the fields generated by the foreach loop are properly validated to see whether they are empty or not. It also properly checks a certain field to see if it's empty. Take note that it's not limited to string.IsNullOrWhiteSpace(specs.Value).
private void FormatOnBlurSpecs(FocusEventArgs e)
{
foreach (var specs in SpecsEntryHolderList)
{
if (string.IsNullOrWhiteSpace(specs.Value))
{
Console.WriteLine($"Spec {specs.Name} (ID={specs.ID}) is empty!");
}
else
{
Console.WriteLine($"Spec {specs.Value} (ID={specs.ID})");
}
}
}
In the example above, I provided a minimal setup to simplify the process. However, in a real application, you should handle message validation for each text field, as they will likely not be the same. For example, each field has its own specific validation that's unique from other fields. However, the tricky part is that the field is generated by a foreach loop, so it's actually a single field.
I was trying to programmatically handle the validation for a certain field when it's empty (again, it's not limited to being an emptiness of the field) through this approach (if it is possible), but I can't achieve what I wanted. I already know that the basic implementation of built-in validation in Blazor is through a set of fields that corresponds to each text entry. The approach I'm trying to achieve is to provide a custom validation instead, with a reconceptualized field binding source. I was trying to figure out this limitation and its corresponding workaround since it is obviously a dynamic setup of InputText.
Here is my additional question, aside from the title question for this context:
- Is there a workaround for this approach? If so, how can it be implemented properly?
