简体   繁体   English

自定义 Blazor 选择组件选项元素 @onclick 未触发 - 如何触发它?

[英]Custom Blazor Select-component option-element @onclick not firing - How do I get it to fire?

I am creating a custom Blazor component, and I need the select component to update the bound field with the selected value, this part is working.我正在创建一个自定义 Blazor 组件,我需要 select 组件来使用所选值更新绑定字段,这部分工作正常。 I also need it to execute a method passed in from the parent component, this part is not working.我还需要它来执行从父组件传入的方法,这部分不起作用。

I am able to pass the method into the custom component (child), but the onclick event of the option element of the select element is not firing.我可以将该方法传递给自定义组件(子组件),但 select 元素的选项元素的onclick事件未触发。

I was able to implement this in a multiple select element, but it is not working in a select element.我能够在多个 select 元素中实现它,但它在 select 元素中不起作用。

How do I get this to fire and still update the bound data property?我如何让它触发并仍然更新绑定数据属性?

ESelect.razor Custom (child) select component ESelect.razor自定义(子)select组件

@inherits InputBase<string>

<div class="form-floating">
        <select class="form-control form-select @CssClass" id="@Id" @bind="@CurrentValue" >
            <option disabled selected></option>
            @foreach(SelectOption option in Options)
            {
                <option id=@option.Id onclick="@( () => OnClick(option) )" >@option.Value</option>
            }
        </select>
        @if (!string.IsNullOrWhiteSpace(Label))
        {
            <label class="form-control-label" for="@Id">@Label</label>        
        }
        <div class="form-control-validation">
            <ValidationMessage For="@ValidationFor" />
        </div>
</div>

ESelect.razor.cs (C# code for ESelect.razor) ESelect.razor.cs (ESelect.razor 的 C# 代码)

using BebComponents.DataModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Linq.Expressions;

namespace BebComponents
{
    public partial class ESelect
    {
        [Parameter, EditorRequired]
        public Expression<Func<string>> ValidationFor { get; set; } = default!;
        [Parameter]
        public string? Id { get; set; } = "ESelect";
        [Parameter]
        public string? Label { get; set; }
        [Parameter]
        public List<SelectOption> Options { get; set; }
        [Parameter]
        public SelectOption? SelectedOption { get; set; }
        [Parameter]
        public Action? Trigger { get; set; }

        protected override bool TryParseValueFromString(string? value, out string result, out string validationErrorMessage)
        {            
            result = value; 
            validationErrorMessage = null;
            return true;
        }

        public void OnClick(SelectOption option)
        {
            SelectedOption = option;            
            Trigger?.Invoke();
        }

    }
}

SelectOption.cs (datamodel) SelectOption.cs (数据模型)

namespace BebComponents.DataModels
{
    public class SelectOption
    {
        public int Id { get; set; }
        public string Value { get; set; }
    }
}

Index.razor

@page "/"
@using BebComponents
@using BebComponents.DataModels
@using static BebComponents.EDualSelect

<EditForm Model="Form" OnValidSubmit="ValidFormSubmit" class="mt-5">
    <DataAnnotationsValidator />
    <h3>Form Example:</h3>
    <ValidationSummary />
    <h3 class="mt-4">Enhanced Select</h3>
    <ESelect Id="ESelect" @ref="eSelect" @bind-Value="Form.LastName" Options="@options" ValidationFor="@( () => Form.LastName )" 
        Label="Last Name" Trigger="EnableEnhancedSelect2"/>
    <h5 class="mt-2">The last name selected is:</h5>
    <p>@Form.LastName</p>
</EditForm>

Index.razor.cs

using BlazorComponents.Pages.PageModels;
using BebComponents;
using static BebComponents.EDualSelect;
using static BebComponents.ESingleSelect;
using static BebComponents.ESelect;
using BebComponents.DataModels;

namespace BlazorComponents.Pages
{    
    public partial class Index
    {
        private ESelect eSelect;
        private string eSelectResult;
        private readonly List<SelectOption> options = new List<SelectOption>
        {            
            new SelectOption { Id = 1, Value = "Jones" },
            new SelectOption { Id = 2, Value = "Smith" },
            new SelectOption { Id = 3, Value = "Bender" },
            new SelectOption { Id = 4, Value = "Baggio" },
            new SelectOption { Id = 5, Value = "Allen" },
            new SelectOption { Id = 6, Value = "Biggs" },
            new SelectOption { Id = 7, Value = "Randall" },
            new SelectOption { Id = 8, Value = "Anderson" },
            new SelectOption { Id = 8, Value = "Reeves" }
        };
    }
}

The Simple Solution简单的解决方案

Your existing code can be made to work with one minor addition.您现有的代码可以通过一个小的添加来工作。

You can see a working example in this Blazor Fiddle Demo Blazor Fiddle is rather limited, but I've tried to keep the code close to the original.您可以在这个Blazor Fiddle Demo中看到一个工作示例 Blazor Fiddle 相当有限,但我尽量使代码接近原始代码。

Your click handler never fires because an <option> element only fires when the parent is a <select multiple> .您的点击处理程序永远不会触发,因为<option>元素仅在父级为<select multiple>时触发。 Yet, in this case it's just a default single <select> and will never fire.然而,在这种情况下,它只是一个默认的单个<select>并且永远不会触发。 We might use the onchange event instead, which is usually how this is done.我们可能会改用 onchange 事件,这通常是这样做的。 Yet, we could also just add a wrapper around the CurrentValue property, which would allow you to reuse the code you already have.然而,我们也可以只在 CurrentValue 属性周围添加一个包装器,这将允许您重用已有的代码。

CurrentValue is a property inherited from InputBase. CurrentValue 是从 InputBase 继承的属性。 And to invoke the parent supplied Trigger method you will need to wrap CurrentValue to provide that new functionality.要调用父提供的 Trigger 方法,您需要包装 CurrentValue 以提供该新功能。 This can be done by using the "new" keyword and calling the method in the setter as shown.这可以通过使用“new”关键字并调用 setter 中的方法来完成,如图所示。

Code to add to the component:添加到组件的代码:

[Parameter]
public new string CurrentValue 
{
    get => base.CurrentValue;
    set {
        base.CurrentValue = value;
        Trigger?.Invoke();
    }
}

You may also want to remove the existing onclick handler from the markup since it doesn't do anything and would only be misleading.您可能还想从标记中删除现有的 onclick 处理程序,因为它不会执行任何操作,只会产生误导。

Updated Answer:更新答案:

Here's a revised answer that shows you how to build out a composite control - in this case a select - and link in the binding.这是一个修改后的答案,它向您展示了如何构建一个复合控件——在本例中为 select——以及绑定中的链接。 I've simplified the options setup.我简化了选项设置。

I've added an extra ValueHasChanged EventCallback that gets triggered whenever the value is changed.我添加了一个额外的ValueHasChanged EventCallback ,只要值发生变化就会被触发。 I use it to print a time update to show it working.我用它来打印时间更新以显示它正在工作。

@typeparam TValue
@using System.Linq.Expressions

<div class="row">
    <div class="col-2">
        @this.Label
    </div>
    <div class="col-6">
        <InputSelect TValue=TValue Value=@this.Value ValueChanged=this.OnValueChanged ValueExpression=this.ValueExpression>
            @ChildContent
        </InputSelect>
    </div>
    <div class="col-6">
        <ValidationMessage For=this.ValidationFor />
    </div>
</div>

@code {
    [Parameter, EditorRequired] public string Label { get; set; } = "Field Label";

    [Parameter] public TValue? Value { get; set; }

    [Parameter] public EventCallback<TValue> ValueChanged { get; set; }

    [Parameter] public Expression<Func<TValue>>? ValueExpression { get; set; }

    [Parameter, EditorRequired] public Expression<Func<TValue>> ValidationFor { get; set; } = default!;

    [Parameter] public RenderFragment? ChildContent { get; set; }

    [Parameter] public EventCallback<TValue> ValueHasChanged { get; set; }

    private void OnValueChanged(TValue value)
    {
        ValueChanged.InvokeAsync(value);
        if (this.ValueHasChanged.HasDelegate)
            this.ValueHasChanged.InvokeAsync(value);
    }
}

And a test page:和一个测试页:

@page "/"
@using Microsoft.Extensions.Options
<EditForm Model=model>
    <MySelect Label="Country" TValue=int @bind-Value=model.Value ValueHasChanged=this.OnValueChanged ValidationFor="() => model.Value" >
        <option value=1>Spain</option>
        <option value=2>Portugal</option>
        <option value=3>France</option>
    </MySelect>
</EditForm>

<div class="m-2 p-2">
    Value = @model.Value
</div>
<div class="m-2 p-2">
    Value = @message
</div>

@code {
    private MyModel model = new();
    private string message = string.Empty;

    private void OnValueChanged(int value)
        =>  this.message = $"Updated at {DateTime.Now.ToLongTimeString()}";

    public class MyModel 
    {
        public int Value { get; set; } = 3;
    }
}

Original Answer:原答案:

Your code is bit complex to decipher.您的代码很难破译。 Here's a simple example to demonstrate how to use a select and capture the change event on it.下面是一个简单示例,演示如何使用 select 并捕获其上的更改事件。

The important bits to understand are:要理解的重要部分是:

  1. You can't bind to an Option.您不能绑定到Option.

  2. Binding with @bind uses the Onchanged event on the select so you can't also bind to it.与@bind 的绑定使用@bind上的Onchanged事件,因此您不能同时绑定到它。 Instead use @oninput to bind to an event handler.而是使用@oninput绑定到事件处理程序。

  3. The value returned is a string, so you need to do some parsing to get it to say an int.返回的值是一个字符串,因此您需要进行一些解析以使其成为一个 int。

  4. If you're triggering an event you need to check that the value has actually changed and only call the Event Callback if it has.如果您正在触发一个事件,您需要检查该值是否实际发生了变化,并且只有在发生变化时才调用 Event Callback。

@page "/"
<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<select @bind=this.Value @oninput=OnChange>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
</select>

<div>
    value: @this.Value
</div>

<div>
    Int Value: @this.IntValue
</div>

@code {
    public EventCallback<int> OnValueChanged;

    private string Value = "3";

    private int IntValue;

    private async Task OnChange(ChangeEventArgs e)
    {
        var x = e.Value;
        if (int.TryParse(e.Value?.ToString(), out int value))
        {
            if (value != IntValue)
            {
            this.IntValue = value;
            if (this.OnValueChanged.HasDelegate)
                await this.OnValueChanged.InvokeAsync(value);
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 Kendo Blazor TreeList 元素中获取一棵树? - How do I get a tree in Kendo Blazor TreeList element? Blazor:如何为多个枚举类使用 Select Option Razor 组件? - Blazor: How to Use a Select Option Razor Component for Multiple Enum Classes? 对于服务器端 Blazor,如何在变量更改时触发 function? - With server side Blazor, how do I get a function to fire when a variable is changed? 如何为 blazor 组件文本框触发 ValidateOninput - How to fire ValidateOninput for blazor component Text box 如何在 blazor 组件中显示来自后端的自定义引导警报 - How do I display a custom bootstrap alert from backend in blazor component Blazor UI 问题,当用户没有 select 选项时,如何在下拉菜单 select 语句中强制显示所需的错误消息? - Blazor UI issue, how do I force the required error message on my dropdown select statement when user does not select an option? 如何在 Blazor 中使用 SELECT 组件进行双向绑定? - How to get two-way binding in Blazor with a SELECT Component? 如何从另一个 Blazor(“子”)组件调用一个 Blazor(父)组件中的方法? - How do I call a method in one Blazor (parent) component from another Blazor (“child”) component? 如何绑定对象<select>Blazor 中的选项? - How to bind object to <select> option in Blazor? 如何从 blazor ElementReference 获取 HTML 元素 - How can I get an HTML element from a blazor ElementReference
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM