自定义组件上的 Blazor 双向绑定

Dre*_*yre 11 c# .net-core blazor blazor-server-side blazor-client-side

我正在创建一个 blazor 服务器端应用程序,但在两个自定义组件之间绑定值时遇到问题。

我已经查看了有关 bind 或 @bind 应该如何工作的不同示例,但我无法弄清楚有关此事的最新信息是什么。

给定一个模型类 User:

public class User
    {
        [Mapping(ColumnName = "short_id")]
        public string ShortId { get; set; }

        public User()
        {

        }
    }
Run Code Online (Sandbox Code Playgroud)

我想构建一个表单,显示该用户的所有属性并将它们输入到输入中,以便可以对其进行编辑并最终保存到数据库中。

我的表单(父组件)如下所示:

<div class="edit-user-form">
    <AnimatedUserInput TbText="@User.ShortId" Placeholder="MHTEE Id"/>
    <button class="btn btn-secondary" @onclick="Hide" style="{display:inline-block;}">Back</button>
    <button class="btn btn-primary" @onclick="SaveUser" style="{display:inline-block;}">Save</button>
</div>
@code {
    [Parameter] public vUser User { get; set; }

    [Parameter] public Action Hide { get; set; }

    bool _error = false;

    void SaveUser()
    {
        ValidateUserData();
    }

    void ValidateUserData()
    {
        _error = string.IsNullOrWhiteSpace(User.ShortId);
    }
}
Run Code Online (Sandbox Code Playgroud)

AnimatedUserInput看起来像这样的自定义组件在哪里:

<div class="edit-area @FocusClass @WiggleClass">
    <input type="text" @bind="@TbText" />
    <span data-placeholder="@Placeholder"></span>
</div>
@code {
    [Parameter]
    public string TbText { get; set; }

    [Parameter]
    public string Placeholder { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在在输入文本框中,我正确地看到了父组件ShortId中的User对象。

但是,如果我更改输入中的文本并单击Save触发该ValidateUserData方法并允许我查看当前User对象的按钮,我会看到实际User.ShortId属性中没有任何更改,而仅在输入上进行。

有没有办法绑定它,以便输入中的更改将自动应用于绑定的属性?

我有几个需要在表单中显示的OnChanged属性,这就是为什么我不想为每个属性挂钩自定义事件。

Dre*_*yre 20

好吧,对于任何绊倒这一点的人来说。我尝试了更多并找到了解决方案。因此,对于我的自定义输入组件,AnimatedUserInput我添加了一个 EventCallback,每次更新输入值时都会调用它:

@code {

    [Parameter]
    public string TbText
    {
        get => _tbText;
        set
        {
            if (_tbText == value) return;

            _tbText = value;
            TbTextChanged.InvokeAsync(value);
        }
    }

    [Parameter]
    public EventCallback<string> TbTextChanged { get; set; }

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

    private string _tbText;
}
Run Code Online (Sandbox Code Playgroud)

我的父组件上的绑定如下所示:

<div class="edit-user-form">
    <AnimatedUserInput @bind-TbText="@User.ShortId" Placeholder="MHTEE Id"/>
    <AnimatedUserInput @bind-TbText="@User.FirstName" Placeholder="First name" />
    <AnimatedUserInput @bind-TbText="@User.LastName" Placeholder="Last name" />
    <AnimatedUserInput @bind-TbText="@User.UserName" Placeholder="Username" />
    <AnimatedUserInput @bind-TbText="@User.StaffType" Placeholder="Staff type" />
    <AnimatedUserInput @bind-TbText="@User.Token" Placeholder="Token" />
    <button class="btn btn-secondary" @onclick="Hide" style="{display:inline-block;}">Back</button>
    <button class="btn btn-primary" @onclick="SaveUser" style="{display:inline-block;}">Save</button>
</div>

@code {
    [Parameter] public vUser User { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,blazor 可以正确地将值绑定在一起,并按照我希望它们绑定的方式从两侧更新它们。

  • 这让我抓狂,谢谢你的提示!我缺少字符串值上的本地集来调用更改。这也是一个很难搜索的问题。 (2认同)

Gre*_*reg 15

Blazor 的一些数据绑定资源没有提到,当您执行 @bind-Value 时,您实际上会自动映射到 3 个参数:

绑定呼叫

<CustomComponent TValue="DateTime?" @bind-Value="order.PurchaseDate"></CustomComponent>
Run Code Online (Sandbox Code Playgroud)

自定义组件

@using System.Linq.Expressions;
@typeparam TValue
//...
@code {
[Parameter]
public virtual TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
[Parameter]
public Expression<Func<TValue>> ValueExpression { get; set; }
//...
}
Run Code Online (Sandbox Code Playgroud)

如果您想要一个简化用户界面的组件,了解这一点很重要,因为您可能需要该值表达式来进行表单验证......

<ValidationMessage For="ValueExpression" ></ValidationMessage>
Run Code Online (Sandbox Code Playgroud)

在类似的意义上,我使用这个 ValueExpression 访问器方法来运行我的标签,当然,您可能需要模型数据注释,就像您可能想知道该字段是否有最大长度,或者是必需的......

@( DisplayName.For(ValueExpression) )
Run Code Online (Sandbox Code Playgroud)

代码的显示名称:

public static class StringHelpers
{
    public static string CamelCaseToPhrase(this string self)
    {
        return Regex.Replace(self, "([A-Z])", " $1").Trim();
    }
}

public static class DisplayName
{
    public static string For(string input)
    {
        return input.CamelCaseToPhrase();
    }

    public static string For<T>(Expression<Func<T>> accessor)
    {
        var expression = (MemberExpression)accessor.Body;
        var value = expression.Member.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute;
        return value?.Name ?? expression.Member.Name?.CamelCaseToPhrase() ?? "";
    }

}
Run Code Online (Sandbox Code Playgroud)

我在这里分享这个是因为当我搜索这个时谷歌会把我带到这里,而大多数其他资源在提供这个完整的图片方面做得很糟糕,所以希望这对其他人也有帮助。特别是如果您想访问数据注释,该模式也包含在内。