简体   繁体   English

何时在 Blazor 中使用 ValueChanged 和 ValueExpression?

[英]When to use ValueChanged and ValueExpression in Blazor?

I'm seeing this common pattern in some libraries (MatBlazor, Telerik) of having ValueChanged and ValueExpression properties and it really confuses me.我在某些库(MatBlazor、Telerik)中看到这种具有ValueChangedValueExpression属性的常见模式,这让我很困惑。

What is the difference between both?两者有什么区别? And when to use it?什么时候使用它?

I would like to add a few use cases for ValueChanged and ValueExpression ,我想为ValueChangedValueExpression添加一些用例,

First of all, as enet said, these properties are more like a trinity of properties where you have Foo , FooChanged and FooExpression and it's used in the two-way data bind eg @bind-Foo="SomeProperty" .首先,正如 enet 所说,这些属性更像是一个三位一体的属性,其中有FooFooChangedFooExpression ,它用于双向数据绑定,例如@bind-Foo="SomeProperty"

To create a custom component with a property that can be used with @bind- you need to provide these 3 properties (only providing Foo and FooChanged also work) as [Parameter] and call FooChanged when the property inside your custom component changes.要创建具有可与使用的属性自定义组件@bind-您需要提供这些3个属性(仅提供FooFooChanged也工作)的[Parameter]和呼叫FooChanged当你的自定义组件的改变里面的属性。

eg from enet例如来自 enet

[Parameter]
public TValue Foo
{
    get => text
    set
    {
        if (text != value) {
            text = value;
            if (FooChanged.HasDelegate)
            {
                FooChanged.InvokeAsync(value);
            }
        }
    }
}

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

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

Adding the @bind-Foo would be the same as passing Value and ValueChanged , the only difference is that @bind- will only set the property, but if you add your own ValueChanged , you can do anything you want (Validating, Changing the value to set, etc).添加@bind-Foo将与传递ValueValueChanged相同,唯一的区别是@bind-只会设置属性,但是如果您添加自己的ValueChanged ,您可以做任何您想做的事情(验证,更改值设置等)。

Use cases用例

1 - Creating a component that wraps another component with @bind- 1 - 创建一个组件,用@bind-包装另一个组件

If you have an component that already have a @bind-Foo and you want to create a component on top of that and still pass as parameter @bind-Foo , you can have only one property and pass to @bind-Foo , you need to pass properties to Foo , FooChanged and/or FooExpression .如果你有一个已经有@bind-Foo的组件,并且你想在它之上创建一个组件并且仍然作为参数@bind-Foo传递,你只能有一个属性并传递给@bind-Foo ,你需要将属性传递给FooFooChanged和/或FooExpression

eg例如

CustomInputWrapper.razor CustomInputWrapper.razor

<div>
    <p>My custom input wrapper</p>
    @* If you pass @bind-Value it won't work*@
    @* You need to pass the properties that are used in the bind*@
    <InputText Text="@Value" TextChanged="@ValueChanged" TextExpression="@ValueExpression" />
</div>

@code {    
    [Parameter]
    public virtual string Value { get; set; }

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

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

These situation of wrapping another component will happen a lot if you are making a lot of custom components or don't want to use directly some third party component.如果您正在制作大量自定义组件或不想直接使用某些第三方组件,那么这种包装另一个组件的情况会发生很多。

Example of my project: In my project I'm using MatBlazor and Telerik, but not all of the components in both libraries are completely stable, so I created a wrapper around all of the components and one day, when one of these libraries is completely stable, I will change to use only one library.我的项目示例:在我的项目中,我使用 MatBlazor 和 Telerik,但并非两个库中的所有组件都完全稳定,因此我围绕所有组件创建了一个包装器,有一天,当这些库中的一个完全稳定时稳定,我会改为只使用一个库。 Doing this allow me to have my custom components and if I want to change one, I only change one thing In my custom component and changes the whole application.这样做允许我拥有自定义组件,如果我想更改一个组件,我只更改自定义组件中的一件事并更改整个应用程序。

2 - Adding default value 2 - 添加默认值

If you want to have a default value inside a custom component , you "can" just pass a default value to the property.如果您想在自定义组件中有一个默认值,您“可以”只将一个默认值传递给该属性。

[Parameter]
public virtual DateTime Value { get; set; } = new DateTime(/* some default value*/);

But this have a big problem if you use this component inside a form.但是如果你在表单中使用这个组件,这会有一个大问题。

Why ?为什么 Because you will only change the value inside your component, but if a property is passed in @bind-Value it won't be changed.因为您只会更改组件内部的值,但如果在@bind-Value传递属性,则不会更改。

To add this default value and make it work in the two-way data bind, you need to call ValueChanged and pass the default value.要添加此默认值并使其在双向数据绑定中工作,您需要调用ValueChanged并传递默认值。 This will make your component have the default value and will also change any property in @bind-Value to have the default value.这将使您的组件具有默认值,并且还将更改@bind-Value任何属性以具有默认值。

eg例如

// Lifecycle after all parameters are set
protected override void OnParametersSet()
{
    // Check if the ValueChanged is set
    if (ValueChanged.HasDelegate)
    {
        ValueChanged.InvokeAsync(DateTime.Now);
    }
}

3 - Use case where you really need FooExpression 3 - 真正需要FooExpression用例

When you have an nullable type, eg int?当您有一个可为空类型时,例如int? , sometimes, when the value is null , it can't know it's type, so you need to pass FooExpression so it can get the type by reflection. ,有时,当值为null ,它无法知道它的类型,因此您需要传递FooExpression以便它可以通过反射获取类型。 Here is an example where you need to use it.这是您需要使用它的示例


The use case of these properties will be used more if you are making custom components and have to work with binded property or change on how the bind will work.如果您正在制作自定义组件并且必须使用绑定属性或更改绑定的工作方式,则这些属性的用例将被更多地使用。

If you are only using already made components, it will be rare the cases where you will have to use it.如果您只使用已经制作好的组件,那么您很少会需要使用它。

Actually, you've forgotten the third element of this pattern: Value .实际上,您已经忘记了该模式的第三个元素: Value This "trinity" of properties is frequently used for component two-way data binding.这种“三位一体”的属性经常用于组件双向数据绑定。 Notably, these properties are employed inside the built-in Blazor form components, such as <InputText> .值得注意的是,这些属性在内置 Blazor 表单组件中使用,例如<InputText>

Let's look at an example:让我们看一个例子:

<InputText @bind-Value="employee.FirstName" />
  1. Value is a property provided in the form of @bind-Value="model.PropertyName" . Value是一个以@bind-Value="model.PropertyName"形式提供的属性。

  2. ValueChanged is of type EventCallback<TValue> . ValueChangedEventCallback<TValue>类型。 It stands for a callback that updates the bound value.它代表更新绑定值的回调。 As you can see, we do not use it in the above example—it's not necessary.如您所见,我们在上面的示例中没有使用它——它没有必要。 The compiler knows its job and it takes care of this, meaning that it adds an EventCallback "delegate" with all the necessary settings behind your back.编译器知道它的工作并处理它,这意味着它会添加一个EventCallback “委托”,并在您的背后提供所有必要的设置。

  3. ValueExpression , finally, refers to an expression that identifies the bound value.最后, ValueExpression是指标识绑定值的表达式。 It is automatically created by the compiler, and you rarely, if ever, have to set it.它由编译器自动创建,您很少(如果有的话)必须设置它。

Now let's compare the above with the code below.现在让我们将上面的代码与下面的代码进行比较。 The following example creates a two-way data binding between a parent component and a child component.以下示例在父组件和子组件之间创建双向数据绑定。 However, instead of using the standard "trinity" ( Value , ValueChanged , ValueExpression ), we will replicate the underlying pattern for ourselves:然而,我们不使用标准的“三位一体”( ValueValueChangedValueExpression ),而是为自己复制底层模式:

ParentComponent.razor: ParentComponent.razor:

<ChildComponent @bind-Text="FirstName" />

@code {
    [Parameter]
    public string FirstName { get; set; }
}

ChildComponent.razor: ChildComponent.razor:

<input @bind="Text" />

@code {
    private string text;

    [Parameter]
    public string Text
    {
        get { return text; }
        set
        {
            if (text != value) {
                text = value;
                if (TextChanged.HasDelegate)
                {
                    TextChanged.InvokeAsync(value);
                }
            }
        }
    }

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

The built-in <InputText> and our custom <ChildComponent> are basically the same!内置的<InputText>和我们自定义的<ChildComponent>基本一致!


To answer your other question...要回答您的其他问题...

When will I use ValueChanged and ValueExpression in Blazor??我什么时候可以使用ValueChangedValueExpression在Blazor? I'm creating a wrapper of an input from another library, is this a case for using this trinity?我正在创建来自另一个库的输入的包装器,这是使用三位一体的情况吗?

As explained above, ValueChanged and ValueExpression are properties defined in Blazor's built-in components, and most of the time you won't need to use them directly.如上所述, ValueChangedValueExpression是在 Blazor 的内置组件中定义的属性,大多数情况下不需要直接使用它们。

Look again at the two components I've defined above: <ParentComponent> and <ChildComponent> .再看看我上面定义的两个组件: <ParentComponent><ChildComponent> Change Text and TextChanged to Value and ValueChanged , and my components are still valid and work correctly.TextTextChanged更改为ValueValueChanged ,我的组件仍然有效并正常工作。 The only difference is in naming.唯一的区别在于命名。 What do I do in the <ChildComponent> ?我在<ChildComponent>做什么? I define a parameter property named Text (stands for Value ).我定义了一个名为Text (代表Value )的参数属性。 As I want to enable two-way data binding between the parent and child components, I also need to define a parameter property called here TextChanged (stands for ValueChanged ).因为我想在父组件和子组件之间启用双向数据绑定,所以我还需要定义一个名为TextChanged的参数属性(代表ValueChanged )。 Text goes to TextChanged , Value goes to ValueChanged , and Year goes to YearChanged . Text转到TextChangedValue转到ValueChanged ,而Year转到YearChanged The naming is only convention.命名只是约定。 The main point is that you have to define a property and an EventCallback of the same data type as the property.要点是您必须定义一个属性和一个与该属性具有相同数据类型的EventCallback

Inside the parent component I provide the property as follows:在父组件中,我提供如下属性:

<ChildComponent @bind-Text="NameOfAPropertyDefinedInTheParentComponent" /> or <ChildComponent @bind-Value="NameOfAPropertyDefinedInTheParentComponent" /> or <ChildComponent @bind-Year="NameOfAPropertyDefinedInTheParentComponent" /> <ChildComponent @bind-Text="NameOfAPropertyDefinedInTheParentComponent" /><ChildComponent @bind-Value="NameOfAPropertyDefinedInTheParentComponent" /><ChildComponent @bind-Year="NameOfAPropertyDefinedInTheParentComponent" />

In my components above, there is also code, as for instance in the child component, that invokes the TextChanged delegate in order to pass a value back to the parent component;在我上面的组件中,还有一些代码,例如在子组件中,它调用TextChanged委托以便将值传递回父组件; this is exactly what the ValueChanged delegate does in the components in which it is defined.这正是ValueChanged委托在定义它的组件中所做的。 But you as a user do not have to use it.但是您作为用户不必使用它。 Look at my components... They work perfectly well.看看我的组件……它们工作得很好。 No need to touch.没必要碰。 If you as a user of my component want to subclass it, then you need to know what you're doing and how to subclass a Blazor component properly.如果您作为我的组件的用户想要子类化它,那么您需要知道自己在做什么以及如何正确地子类化 Blazor 组件。 But my components, partially presented here, are relatively simple.但是我的组件(部分呈现在这里)相对简单。

Suppose you want to create a password input based on <InputText> , which is not only doable but quite easy.假设您想基于<InputText>创建一个密码输入,这不仅可行而且很容易。 In that case, you're not going to change anything but the look of the <InputText> component so that asterisk symbols are displayed instead of normal text.在这种情况下,除了<InputText>组件的外观外,您不会更改任何内容,以便显示星号符号而不是普通文本。 The rest of the component is unchanged.组件的其余部分不变。 You do not need to handle the events and such.您不需要处理事件等。 This, of course, does not mean that a component author will never need to call the EventCallback from somewhere in his code.当然,这并不意味着组件作者永远不需要从他的代码中的某个地方调用EventCallback That said, I have never had a good reason to trigger the ValueChanged delegate when using the <InputText> component.也就是说,在使用<InputText>组件时,我从来没有充分的理由触发ValueChanged委托。 And I only once had to provide a ValueExpression , as the compiler was not able to identify the bound value.我只需要提供一次ValueExpression ,因为编译器无法识别绑定值。 (I'll look for it, and if found I'll post it here...) (我会寻找它,如果找到,我会在此处发布...)

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

相关问题 blazor 中的 ValueChanged 和 ValueEpression? - ValueChanged and ValueEpression in blazor? 如何使用 InputNumber.ValueChanged? - how to use InputNumber.ValueChanged? 如何创建自定义 Blazor 多选 HTML 组件? 错误:MultipleSelect 需要“ValueExpression”参数的值 - How to create a custom Blazor multiple select HTML component? Error: MultipleSelect requires a value for the 'ValueExpression' parameter 创建IntegerUpDown ValueChanged事件时发生编译器错误 - Compiler error when creating IntegerUpDown ValueChanged event 将侦听器与Firebase连接时,防止触发ValueChanged - Prevent triggering ValueChanged when listener is attached with Firebase 手动更改值时,为什么在NumericUpDown控件上未触发ValueChanged事件? - Why is the ValueChanged event not triggered on a NumericUpDown Control When the Value is Changed Manually? 具有ValueChanged事件的c#对象,该事件在更改每个属性时触发 - c# object with a ValueChanged event that fires when each property is changed HiddenField.ValueChanged从Javascript更改时未触发事件 - HiddenField.ValueChanged Event not firing when changed from Javascript 如何在DataGridView中的Cell ValueChanged时运行CellEndEdit - How to run Run CellEndEdit only when Cell ValueChanged in DataGridView 通过Javascript更改时未触发HiddenField EventHandler ValueChanged - HiddenField EventHandler ValueChanged not fired when changed via Javascript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM