如何在Blazor中从类实例传递变量到子组件后进行验证?

huangapple go评论75阅读模式
英文:

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:

&lt;div&gt;
    &lt;label for=&quot;foodName&quot;&gt;Food&lt;/label&gt;
    &lt;div class=&quot;select&quot;&gt;
        &lt;InputSelect id=&quot;foodName&quot;
                     @bind-Value=&quot;FoodId&quot;
                     @oninput=&quot;@(async (args) =&gt; await OnFoodSelectAsync(args?.Value?.ToString() ?? string.Empty))&quot;&gt;
        &lt;/InputSelect&gt;
        &lt;span class=&quot;focus&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
&lt;ValidationMessage For=&quot;@(() =&gt; FoodId)&quot;/&gt;
&lt;/div&gt;

@code {
    
    [Parameter]
    public  EventCallback&lt;string&gt;  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:

&lt;Food  OnFoodSelect=&quot;OnFoodSelectAsync&quot; FoodId=&quot;PerishableFoods.FoodId&quot;/&gt;

or

&lt;Food  OnFoodSelect=&quot;OnFoodSelectAsync&quot; FoodId=&quot;HotFoods.FoodId&quot;/&gt;

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 = &quot;Food selection required&quot;)] 
    public int? FoodId { get; set; }
}    

or

public class PerishableFoods
{
    [Required(ErrorMessage = &quot;Food selection required&quot;)] 
    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; }
    }
}

如何在Blazor中从类实例传递变量到子组件后进行验证?

英文:

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 &lt;span class=&quot;focus&quot;&gt;&lt;/span&gt; is for, and your InputSelect has no content.

The modified component:

@using System.Linq.Expressions;

&lt;div&gt;
    &lt;label for=&quot;foodName&quot;&gt;Food&lt;/label&gt;
    &lt;InputSelect @attributes=AdditionalAttributes
                 TValue=int?
                 Value=this.Value
                 ValueChanged=this.OnChange
                 ValueExpression=this.ValueExpression&gt;

        @if (this.Value is null || this.Value == 0)
        {
            &lt;option selected disabled&gt;@this.PlaceholderText&lt;/option&gt;
        }

        @foreach (var option in SelectItems)
        {
            &lt;option value=&quot;@option.Key&quot;&gt;@option.Value&lt;/option&gt;
        }

    &lt;/InputSelect&gt;
    &lt;ValidationMessage For=@this.ValueExpression /&gt;
&lt;/div&gt;

@code {
    [Parameter] public int? Value { get; set; }
    [Parameter] public EventCallback&lt;int?&gt; ValueChanged { get; set; }
    [Parameter] public Expression&lt;Func&lt;int?&gt;&gt;? ValueExpression { get; set; }

    [Parameter] public IReadOnlyDictionary&lt;int, string&gt; SelectItems { get; set; } = new Dictionary&lt;int, string&gt;();
    [Parameter] public string PlaceholderText { get; set; } = &quot; -- Select an Option -- &quot;;

    [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary&lt;string, object&gt;? AdditionalAttributes { get; set; }

    private Task OnChange(int? value)
        =&gt; this.ValueChanged.InvokeAsync(value);
}

And a page to demostrate the control, editing and validation in action:

@page &quot;/&quot;
@using System.ComponentModel.DataAnnotations;

&lt;PageTitle&gt;Index&lt;/PageTitle&gt;

&lt;h1&gt;Edit Form&lt;/h1&gt;
&lt;h3&gt;Hot Food&lt;/h3&gt;
&lt;EditForm Model=_hotModel OnValidSubmit=this.OnValidSubmit&gt;
    &lt;DataAnnotationsValidator/&gt;
    &lt;div class=&quot;col mb-3&quot;&gt;
        &lt;YourSelect class=&quot;form-control&quot; @bind-Value=_hotModel.FoodId SelectItems=_foods /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;text-end&quot;&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-success&quot;&gt;Submit&lt;/button&gt;
    &lt;/div&gt;
&lt;/EditForm&gt;

&lt;h3&gt;Cold Food&lt;/h3&gt;
&lt;EditForm Model=_coldModel OnValidSubmit=this.OnValidSubmit&gt;
    &lt;DataAnnotationsValidator /&gt;
    &lt;div class=&quot;col mb-3&quot;&gt;
        &lt;YourSelect class=&quot;form-control&quot; @bind-Value=_coldModel.FoodId SelectItems=_foods /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;text-end&quot;&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-success&quot;&gt;Submit&lt;/button&gt;
    &lt;/div&gt;
&lt;/EditForm&gt;

&lt;div class=&quot;bg-dark text-white m-2 p-2&quot;&gt;
    &lt;pre&gt;Hot Food ID: @_hotModel.FoodId &lt;/pre&gt;
    &lt;pre&gt;Cold Food ID: @_coldModel.FoodId &lt;/pre&gt;
&lt;/div&gt;

@code {
    private HotFoods _hotModel = new();
    private PerishableFoods _coldModel = new();

    private IReadOnlyDictionary&lt;int, string&gt; _foods = new Dictionary&lt;int, string&gt; { { 1, &quot;Bread Loaf&quot; }, { 2, &quot;Pasty&quot;}, { 3, &quot;Sausage Roll&quot;} };

    public Task OnValidSubmit()
    {
        return Task.CompletedTask;
    }

    public class HotFoods
    {
        [Required(ErrorMessage = &quot;Hot Food selection required&quot;)]
        public int? FoodId { get; set; }
    }

    public class PerishableFoods
    {
        [Required(ErrorMessage = &quot;Cold Food selection required&quot;)]
        public int? FoodId { get; set; }
    }
}

如何在Blazor中从类实例传递变量到子组件后进行验证?

huangapple
  • 本文由 发表于 2023年7月3日 14:33:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76602330.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定