英文:
How do I get a variable to validate after being passed in from a class instance to a child component in Blazor
问题
我有一个通用组件,在代码中由三个不同的页面使用。它叫做 Food:
<div>
<label for="foodName">Food</label>
<div class="select">
<InputSelect id="foodName"
@bind-Value="FoodId"
@oninput="@(async (args) => await OnFoodSelectAsync(args?.Value?.ToString() ?? string.Empty))">
</InputSelect>
<span class="focus"></span>
</div>
<ValidationMessage For="@(() => FoodId)"/>
</div>
@code {
[Parameter]
public EventCallback<string> OnFoodSelect { get; set; }
[Parameter]
public int? FoodId { get; set; }
async Task OnFoodSelectAsync(string selectedFood)
{
if (OnFoodSelect.HasDelegate)
{
await OnFoodSelect.InvokeAsync(selectedFood);
}
}
}
这些页面像这样使用它:
<Food OnFoodSelect="OnFoodSelectAsync" FoodId="PerishableFoods.FoodId"/>
或
<Food OnFoodSelect="OnFoodSelectAsync" FoodId="HotFoods.FoodId"/>
我希望来自 HotFoods 或 PerishableFoods 的 FoodId 的验证可以在子组件中工作。类中的注释如下:
public class HotFoods
{
[Required(ErrorMessage = "Food selection required")]
public int? FoodId { get; set; }
}
或
public class PerishableFoods
{
[Required(ErrorMessage = "Food selection required")]
public int? FoodId { get; set; }
}
然而,ValidationMessage 目前不起作用。唯一起作用的是 ValidationSummary,但我不想要列表式的功能。每个错误消息应该在其输入后立即显示。是否可以进行编辑以使此验证起作用?
我还尝试过使用 ObjectGraphDataAnnotationsValidator 的 ValidateComplexType,但也无法使其工作。
英文:
I have a generic component that is used by three different pages in the code. Its called Food:
<div>
<label for="foodName">Food</label>
<div class="select">
<InputSelect id="foodName"
@bind-Value="FoodId"
@oninput="@(async (args) => await OnFoodSelectAsync(args?.Value?.ToString() ?? string.Empty))">
</InputSelect>
<span class="focus"></span>
</div>
<ValidationMessage For="@(() => FoodId)"/>
</div>
@code {
[Parameter]
public EventCallback<string> OnFoodSelect { get; set; }
[Parameter]
public int? FoodId { get; set; }
async Task OnFoodSelectAsync(string selectedFood)
{
if (OnFoodSelect.HasDelegate)
{
await OnFoodSelect.InvokeAsync(selectedFood);
}
}
}
Those pages use it like so:
<Food OnFoodSelect="OnFoodSelectAsync" FoodId="PerishableFoods.FoodId"/>
or
<Food OnFoodSelect="OnFoodSelectAsync" FoodId="HotFoods.FoodId"/>
I want the validation of FoodId from HotFoods or PerishableFoods to work in the child component. The annotation in the classes look like:
public class HotFoods
{
[Required(ErrorMessage = "Food selection required")]
public int? FoodId { get; set; }
}
or
public class PerishableFoods
{
[Required(ErrorMessage = "Food selection required")]
public int? FoodId { get; set; }
}
However, the ValidationMessage doesn't work right now. The only thing that works is ValidationSummary and I don't want a list kind of functionality. Each error message should show up right after its input. Are there edits I can make to make this validation work?
I have also tried ValidateComplexType with ObjectGraphDataAnnotationsValidator but I can't get that to work either.
答案1
得分: 0
I've taken the liberty of rewriting your select component to demonstrate how you can incorporate binding so it has the standard Blazor input functionality. And you get back simple object validation. I've also added a little extra functionality for dealing with null/zero values.
I'm not sure what the <span class="focus"></span>
is for, and your InputSelect
has no content.
The modified component:
@using System.Linq.Expressions;
<div>
<label for="foodName">Food</label>
<InputSelect @attributes=AdditionalAttributes
TValue=int?
Value=this.Value
ValueChanged=this.OnChange
ValueExpression=this.ValueExpression>
@if (this.Value is null || this.Value == 0)
{
<option selected disabled>@this.PlaceholderText</option>
}
@foreach (var option in SelectItems)
{
<option value="@option.Key">@option.Value</option>
}
</InputSelect>
<ValidationMessage For=@this.ValueExpression />
</div>
@code {
[Parameter] public int? Value { get; set; }
[Parameter] public EventCallback<int?> ValueChanged { get; set; }
[Parameter] public Expression<Func<int?>>? ValueExpression { get; set; }
[Parameter] public IReadOnlyDictionary<int, string> SelectItems { get; set; } = new Dictionary<int, string>();
[Parameter] public string PlaceholderText { get; set; } = " -- Select an Option -- ";
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
private Task OnChange(int? value)
=> this.ValueChanged.InvokeAsync(value);
}
And a page to demonstrate the control, editing, and validation in action:
@page "/"
@using System.ComponentModel.DataAnnotations;
<PageTitle>Index</PageTitle>
<h1>Edit Form</h1>
<h3>Hot Food</h3>
<EditForm Model=_hotModel OnValidSubmit=this.OnValidSubmit>
<DataAnnotationsValidator/>
<div class="col mb-3">
<YourSelect class="form-control" @bind-Value=_hotModel.FoodId SelectItems=_foods />
</div>
<div class="text-end">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</EditForm>
<h3>Cold Food</h3>
<EditForm Model=_coldModel OnValidSubmit=this.OnValidSubmit>
<DataAnnotationsValidator />
<div class="col mb-3">
<YourSelect class="form-control" @bind-Value=_coldModel.FoodId SelectItems=_foods />
</div>
<div class="text-end">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</EditForm>
<div class="bg-dark text-white m-2 p-2">
<pre>Hot Food ID: @_hotModel.FoodId </pre>
<pre>Cold Food ID: @_coldModel.FoodId </pre>
</div>
@code {
private HotFoods _hotModel = new();
private PerishableFoods _coldModel = new();
private IReadOnlyDictionary<int, string> _foods = new Dictionary<int, string> { { 1, "Bread Loaf" }, { 2, "Pasty" }, { 3, "Sausage Roll" } };
public Task OnValidSubmit()
{
return Task.CompletedTask;
}
public class HotFoods
{
[Required(ErrorMessage = "Hot Food selection required")]
public int? FoodId { get; set; }
}
public class PerishableFoods
{
[Required(ErrorMessage = "Cold Food selection required")]
public int? FoodId { get; set; }
}
}
英文:
I've taken the liberty of rewriting your select component to demonstrate how you can incorporate binding so it has the standard Blazor input functionality. And you get back simple object validation. I've also added a little extra functionality for dealing with null/zero values.
I'm not sure what the <span class="focus"></span>
is for, and your InputSelect
has no content.
The modified component:
@using System.Linq.Expressions;
<div>
<label for="foodName">Food</label>
<InputSelect @attributes=AdditionalAttributes
TValue=int?
Value=this.Value
ValueChanged=this.OnChange
ValueExpression=this.ValueExpression>
@if (this.Value is null || this.Value == 0)
{
<option selected disabled>@this.PlaceholderText</option>
}
@foreach (var option in SelectItems)
{
<option value="@option.Key">@option.Value</option>
}
</InputSelect>
<ValidationMessage For=@this.ValueExpression />
</div>
@code {
[Parameter] public int? Value { get; set; }
[Parameter] public EventCallback<int?> ValueChanged { get; set; }
[Parameter] public Expression<Func<int?>>? ValueExpression { get; set; }
[Parameter] public IReadOnlyDictionary<int, string> SelectItems { get; set; } = new Dictionary<int, string>();
[Parameter] public string PlaceholderText { get; set; } = " -- Select an Option -- ";
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
private Task OnChange(int? value)
=> this.ValueChanged.InvokeAsync(value);
}
And a page to demostrate the control, editing and validation in action:
@page "/"
@using System.ComponentModel.DataAnnotations;
<PageTitle>Index</PageTitle>
<h1>Edit Form</h1>
<h3>Hot Food</h3>
<EditForm Model=_hotModel OnValidSubmit=this.OnValidSubmit>
<DataAnnotationsValidator/>
<div class="col mb-3">
<YourSelect class="form-control" @bind-Value=_hotModel.FoodId SelectItems=_foods />
</div>
<div class="text-end">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</EditForm>
<h3>Cold Food</h3>
<EditForm Model=_coldModel OnValidSubmit=this.OnValidSubmit>
<DataAnnotationsValidator />
<div class="col mb-3">
<YourSelect class="form-control" @bind-Value=_coldModel.FoodId SelectItems=_foods />
</div>
<div class="text-end">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</EditForm>
<div class="bg-dark text-white m-2 p-2">
<pre>Hot Food ID: @_hotModel.FoodId </pre>
<pre>Cold Food ID: @_coldModel.FoodId </pre>
</div>
@code {
private HotFoods _hotModel = new();
private PerishableFoods _coldModel = new();
private IReadOnlyDictionary<int, string> _foods = new Dictionary<int, string> { { 1, "Bread Loaf" }, { 2, "Pasty"}, { 3, "Sausage Roll"} };
public Task OnValidSubmit()
{
return Task.CompletedTask;
}
public class HotFoods
{
[Required(ErrorMessage = "Hot Food selection required")]
public int? FoodId { get; set; }
}
public class PerishableFoods
{
[Required(ErrorMessage = "Cold Food selection required")]
public int? FoodId { get; set; }
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论