【问题标题】:Changed value not being sent to a Blazor component's parent更改的值未发送到 Blazor 组件的父级
【发布时间】:2021-03-19 04:38:21
【问题描述】:

注意为了更清楚我在做什么,我添加了sample project to github。这显示了FormRowText 组件的使用,它工作正常,而FormRowDropdownEnum 组件,它不起作用。如果有人能够下载这个项目并看看我做错了什么,那就太好了。

我正在尝试编写一系列可重用的 Blazor 组件来布置表单控件。其中大多数都在工作(例如<input>),但其中一个给我带来了问题。想法是将enum的类型作为参数传递,组件将根据enum的值创建<select>

我做了一个通用组件如下(.razor first)...

@typeparam T

<div class="form-group row">
  <label for="@PropertyName" class="col-lg-2 col-form-label">@(Caption ?? PropertyName)</label>
  <div class="col-lg-10 input-group">
    <div class="input-group-prepend">
      <span class="input-group-text">
        <i class="far fa-@Icon"></i>
      </span>
    </div>
    <select class="form-control" name="@PropertyName" id="@PropertyName" @onchange="@OnChanged">
      @foreach (T v in Enum.GetValues(typeof(T)).Cast<T>()) {
        if ((int)Enum.Parse(typeof(T), v.ToString()) == Value) {
          <option value="@Convert.ToInt32(v)" selected>@Enum.GetName(typeof(T), v)</option>
        } else {
          <option value="@Convert.ToInt32(v)">@Enum.GetName(typeof(T), v)</option>
        }
      }
    </select>
  </div>
</div>

后面的代码是这样的……

  public partial class FormRowDropdownEnum<T> where T : Enum, IConvertible {
    [Parameter]
    public int Value { get; set; }

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

    [Parameter]
    public string PropertyName { get; set; }

    [Parameter]
    public string Caption { get; set; }

    [Parameter]
    public string Icon { get; set; }

    private async Task OnChanged(ChangeEventArgs cea) {
      int val = (int)Enum.Parse(typeof(T), cea.Value?.ToString() ?? "0");
      await ValueChanged.InvokeAsync(val);
    }
  }

它的用法如下(假设_customer是一个模型,它有一个Frequency参数,它的类型是Frequency(一个enum)...

<FormRowDropdownEnum PropertyName="@nameof(Customer.Frequency)"
      T="Frequency" @bind-Value="(int)_customer.Frequency" Icon="wave-sine" />

这会正确显示&lt;select&gt;,并根据模型的Frequency 参数设置适当的&lt;option&gt;

&lt;select&gt;中的选择发生变化时,调用OnChanged方法,并将正确的值作为事件回调的参数。但是,包含此组件的 Blazor 页面(即上面显示的具有&lt;FormRowDropdownEnum&gt; 标记的页面)永远不会看到新值,并且那里的模型没有更新。我在模型的 setter 上放了一个断点,它从未被命中。

正如我所说,我有用于其他输入类型的类似组件,并且它们工作正常,所以我不确定我在这里做错了什么。

有人可以帮忙吗?谢谢

更新根据enet的回复,我尝试在后面的代码中添加一个私有变量,并在输入值更改时对其进行修改...

    private int _value;
    protected override void OnParametersSet() =>
      _value = Value;

    private async Task OnChanged(ChangeEventArgs cea) {
      int val = (int)Enum.Parse(typeof(T), cea.Value?.ToString() ?? "0");
      _value = val;
      await ValueChanged.InvokeAsync(val);
    }

我将 Razor 标记更改为使用 _value 而不是 Value,如下所示...

if ((int)Enum.Parse(typeof(T), v.ToString()) == _value) {

但是,这样做的结果是,当我在下拉菜单中更改选择时,它立即恢复为之前的值,并且父页面再也没有看到新的值。

【问题讨论】:

    标签: blazor


    【解决方案1】:

    这里是your repo working

    FormRowDropdownEnum.razor

    @typeparam TEnum
    
    <div class="form-group row">
      <label for="@PropertyName" class="col-lg-2 col-form-label">@(Caption ?? PropertyName)</label>
      <div class="col-lg-10 input-group">
        <div class="input-group-prepend">
          <span class="input-group-text">
            <i class="far fa-@Icon"></i>
          </span>
        </div>
        <select class="form-control" name="@PropertyName" id="@PropertyName" @bind="Value">
            @foreach (var enumValue in EnumValues)
            {
                @ChildContent(enumValue)
            }
        </select>
      </div>
    </div>
    

    FormRowDropdownEnum.razor.cs

    public partial class FormRowDropdownEnum<TEnum>
    {
        private TEnum _value;
        [Parameter] public string PropertyName { get; set; }
        [Parameter] public string Caption { get; set; }
        [Parameter] public string Icon { get; set; }
        [Parameter]
        public TEnum Value
        {
            get => _value;
            set
            {
                if (_value.Equals(value)) return;
                _value = value;
                if (ValueChanged.HasDelegate)
                {
                    ValueChanged.InvokeAsync(Value);
                }
            }
        }
        [Parameter(CaptureUnmatchedValues = true)]
        public Dictionary<string, object> AdditionalAttributes { get; set; }
        [Parameter] public RenderFragment<TEnum> ChildContent { get; set; }
        [Parameter] public EventCallback<TEnum> ValueChanged { get; set; }
        IEnumerable<TEnum> EnumValues => Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
        protected override void OnInitialized()
        {
            if (Value is not Enum) throw new ArgumentException("Value must be of type Enum");
        }
    }
    
    

    Index.razor

    ...
          <FormRowDropdownEnum PropertyName="@nameof(Customer.Frequency)" @bind-Value="_customer.Frequency" Icon="wave-sine" Context="freq">
              <option value="@freq">@freq</option>
          </FormRowDropdownEnum>
    ...
    @code {
    
      readonly Customer _customer = new Customer {
        Name = "Jim",
        Email = "jim@spriggs.org",
        Frequency = Frequency.Monthly
      };
    
      private string _msg = "";
    
      private void OnSubmit() =>
        _msg = $"Customer name: {_customer.Name}, email: {_customer.Email}, frequency: {_customer.Frequency}";
    
    }
    

    【讨论】:

    • 感谢您的回复,但我无法让它工作。我得到一个编译器错误“组件'InputEnum'的子上下文元素'ChildContent'使用与组件'EditForm'的封闭子内容元素'ChildContent'相同的参数名称('context')。指定参数名称,如:' 来解决歧义”。对不起,如果我很愚蠢,但我对 Blazor 还很陌生,不知道这意味着什么。
    • 另外,我对你的用法感到困惑。如果您查看我最初的问题,您会看到我对其他组件的使用方式。我希望这个以同样的方式工作。再次感谢
    • 如果您在另一个上下文中使用它,您需要命名其中一个。
    • 感谢更新项目。我看到它有效,但我不明白你的代码。您在 Index.razor 中的哪个位置告诉组件枚举类型?在我的代码中,我有 T 参数,但您已将其删除。此外,您的代码将枚举值作为字符串返回,例如OneOff,而我的代码将其返回为intint 更容易使用,因为您可以轻松地将其转换为枚举,并且数据库将值存储为 int 无论如何。如果父页面获得一个字符串,则必须将其转换为枚举值。有没有办法解决?再次感谢。#
    • @AvrohomYisroel 附加属性允许属性飞溅...使您的组件更加灵活。 docs.microsoft.com/en-us/aspnet/core/blazor/components/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-13
    • 1970-01-01
    • 2017-11-13
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    相关资源
    最近更新 更多