英文:
Difference between Value and @bind-Value?
问题
我正在查看有关 InputCheckBox https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.forms.inputcheckbox 的文档,我看到它公开了 Value 以绑定到所需的布尔值("获取或设置输入的值。应与双向绑定一起使用。")。然而,到处都是人们使用 @bind-Value,此外,我无法使 Value 工作。
这个:
<InputCheckbox @bind-Value="model.IsSelected"></InputCheckbox>
与这个有何不同(为什么这个不起作用):
<InputCheckbox Value="@model.IsSelected"></InputCheckbox>
我还注意到 @bind-Value 会更新/通知模型有关更改,并更新任何依赖于 IsSelected 的属性,而 Value 不会(除非明确指定?)。
另外,当使用 Value 时,我需要为标签包括一个 ValueExpression(否则它不会呈现)。这个 ValueExpression 是什么?在哪些情况下会有人实现不同的 ValueExpression?
使用 Value 有什么好处吗?如何使其工作?我有遗漏什么吗?
英文:
I am looking at the docs for an InputCheckBox https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.forms.inputcheckbox, and I see that it exposes Value to be bound to a desired boolean (Gets or sets the value of the input. This should be used with two-way binding.). Nonetheless, everywhere, people are using @bind-Value instead, Moreover, I cannot get Value to work.
How is this:
<InputCheckbox @bind-Value="model.IsSelected"></InputCheckbox>
Different from this (And why this one does not work):
<InputCheckbox Value="@model.IsSelected"></InputCheckbox>
I also noted that @bind-Value updates/notifies the model about the changes and updates any property that depends on IsSelected, while Value does not (probably unless explicitly specified?).
Additionally, when using Value, I need to also include a ValueExpression for the tag (or it won't render). What this ValueExpression?? In which scenarios would someone implement a different ValueExpression?
Has using Value any benefit? What will it take to make it work? Am I missing something here?
答案1
得分: 5
以下是翻译好的内容:
背景信息和 InputBase 的解释。
所有继承 InputBase 的组件都实现了三个参数:
Value是控件的“输入”值 - 它是强类型的。ValueChanged是控件的“输出”值:一个带有强类型值的回调函数。ValueExpression是一个定义实际模型对象/属性的Func委托。 它在内部用于创建一个FieldIdentifier对象,该对象用于在EditContext和ValidationStore中标识属性。
此页面演示了两种设置绑定的方式。
第一种是手动设置并将更改链接到回调方法。 当您希望除了设置值之外运行其他代码时,可以使用此方法(我正在设置时间戳)。
第二种使用由 Razor 提供的“语法糖”。 @bind-Value 告诉 Razor 编译器构建一组代码,以将三个参数与一个名为 Value 的公共属性链接到提供的模型属性。
在编译的低级别C#代码中,它们基本上是相同的东西。
这是完整的绑定:
private RenderFragment FirstComponent => __builder =>
{
__builder.OpenComponent<InputCheckbox>(5);
__builder.AddAttribute(6, "class", "form-check");
__builder.AddAttribute(7, "Value", RuntimeHelpers.TypeCheck<Boolean>(this.model.Value));
__builder.AddAttribute(8, "ValueChanged", RuntimeHelpers.TypeCheck<EventCallback<Boolean>>(EventCallback.Factory.Create<Boolean>(this, RuntimeHelpers.CreateInferredEventCallback(this, __value => this.model.Value = __value, this.model.Value)));
__builder.AddAttribute(9, "ValueExpression", RuntimeHelpers.TypeCheck<global::System.Linq.Expressions.Expression<System.Func<Boolean>>(() => this.model.Value));
__builder.CloseComponent();
};
这是手动绑定:
private RenderFragment SecondComponent => __builder =>
{
__builder.OpenComponent<InputCheckbox>(11);
__builder.AddAttribute(12, "class", "form-check");
__builder.AddAttribute(13, "Value", RuntimeHelpers.TypeCheck<global::System.Boolean>(this.model.Value));
__builder.AddAttribute(14, "ValueChanged", RuntimeHelpers.TypeCheck<EventCallback<Boolean>>(EventCallback.Factory.Create<Boolean>(this, this.OnValueChanged)));
__builder.AddAttribute(15, "ValueExpression", RuntimeHelpers.TypeCheck<Expression<System.Func<System.Boolean>>(() => this.model.Value));
__builder.CloseComponent();
};
Net7.0 @input-value:get 和 @input-value:set
Net7.0 提供了更多的语法糖,让您以另一种方式进行绑定。 它还添加了第三个 @input-value:after 绑定,以提供我上面显示的时间戳方法。
请查看此链接获取最新的绑定信息 - https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding
英文:
Some more background information and an explanation of InputBase.
All InputBase inherited components implement three Parameters:
Valueis the "in" value for the control - it's strongly typed.ValueChangedis the "out" value for the control: A callback with a strongly typed value.ValueExpressionis aFuncdelegate that defines the actual model object/property. It's used internally to create aFieldIdentifierobject, which is used to identify the property in theEditContextandValidationStore.
This page demonstrates two ways of setting up the bind.
The first does it manually and hooks up the change to a callback method. You use this when you have other code you want to run in addition to just setting the value. (I'm setting a time stamp).
The second uses "syntatic sugar" provided by Razor. @bind-Value tells the Razor compiler to build out a set of code to link the three Parameters with a common name of Value to te provided model property.
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
<InputCheckbox class="form-check"
@bind-Value=this.model.Value />
<InputCheckbox class="form-check"
Value=this.model.Value
ValueChanged=this.OnValueChanged
ValueExpression="() => this.model.Value" />
<div class="alert alert-info">
Value: @this.model.Value
</div>
<div class="alert alert-info">
@this.message
</div>
@code {
private Model model = new();
private string message = "No Message";
private Task OnValueChanged(bool value)
{
this.model.Value = value;
// You can do other stuff here if you need to
this.message = $"Set at {DateTime.Now.ToLongTimeString()}";
return Task.CompletedTask;
}
public class Model
{
public bool Value { get; set; }
}
}
In the compiled low level C# code they are virtually the same thing.
Here's the full bind:
private RenderFragment FirstComponent => __builder =>
{
__builder.OpenComponent<InputCheckbox>(5);
__builder.AddAttribute(6, "class", "form-check");
__builder.AddAttribute(7, "Value", RuntimeHelpers.TypeCheck<Boolean>(this.model.Value));
__builder.AddAttribute(8, "ValueChanged", RuntimeHelpers.TypeCheck<EventCallback<Boolean>>(EventCallback.Factory.Create<Boolean>(this, RuntimeHelpers.CreateInferredEventCallback(this, __value => this.model.Value = __value, this.model.Value))));
__builder.AddAttribute(9, "ValueExpression", RuntimeHelpers.TypeCheck<global::System.Linq.Expressions.Expression<System.Func<System.Boolean>>>(() => this.model.Value));
__builder.CloseComponent();
};
Here's the manual bind:
private RenderFragment SecondComponent => __builder =>
{
__builder.OpenComponent<InputCheckbox>(11);
__builder.AddAttribute(12, "class", "form-check");
__builder.AddAttribute(13, "Value", RuntimeHelpers.TypeCheck<global::System.Boolean>(this.model.Value));
__builder.AddAttribute(14, "ValueChanged", RuntimeHelpers.TypeCheck<EventCallback<Boolean>>(EventCallback.Factory.Create<Boolean>(this, this.OnValueChanged)));
__builder.AddAttribute(15, "ValueExpression", RuntimeHelpers.TypeCheck<Expression<System.Func<System.Boolean>>>(() => this.model.Value));
__builder.CloseComponent();
};
Net7.0 @input-value:get and @input-value:set
Net7.0 implements more syntatic sugar to let you do binding in yet another way. It also adds a third @input-value:after bind to provide a method to do the timestamp I showed above.
See this for the latest bind information - https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding
答案2
得分: 3
如您所注意,@bind-Value 提供了对 Value 的双向绑定。
我曾经创建了以下演示:
-
从模板中新建一个项目
-
稍微修改计数器页面:
<p role="status">当前计数:@Count</p>
<button class="btn btn-primary" @onclick="IncrementCount">点击我</button>
@code {
[Parameter]
public int Count { get; set; } = 1;
[Parameter]
public EventCallback<int> CountChanged { get; set; }
public Task IncrementCount()
{
return CountChanged.InvokeAsync(Count + 1);
}
}
- 然后在主页上:
@page "/"
<div>
<p>计数器1:@count1</p>
<p>计数器2:@count2</p>
</div>
<Counter @bind-Count="count1" />
<Counter Count="count2" CountChanged="UpdateCount2" />
@code {
int count1 = 1;
int count2 = 2;
void UpdateCount2(int newValue)
{
count2 = newValue;
}
}
现在,当您在主页上单击按钮时,两个计数器实例的行为完全相同。
这是因为对于 @bind-Count,Razor 编译器在后台生成了与 CountChanged 参数名称等效的 UpdateCount1 部分。这仅在存在正确名称的回调参数时才有效:CountChanged。
请参阅Blazor数据绑定。
英文:
As you noticed, @bind-Value delivers 2-way binding for Value.
I made the following demo once:
-
Start a new Project from one of the templates
-
Change the Counter Page a little:
<p role="status">Current count: @Count</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int Count { get; set; } = 1;
[Parameter]
public EventCallback<int> CountChanged { get; set; }
public Task IncrementCount()
{
return CountChanged.InvokeAsync(Count + 1);
}
}
- And then on the the Index page:
@page "/"
<div>
<p>Counter1 : @count1</p>
<p>Counter2 : @count2</p>
</div>
<Counter @bind-Count="count1" />
<Counter Count="count2" CountChanged="UpdateCount2" />
@code {
int count1 = 1;
int count2 = 2;
void UpdateCount2(int newValue)
{
count2 = newValue;
}
}
When you now click the buttons on the Index page both Counter instances behave exactly the same.
This is because for @bind-Count the Razor compiler generates the UpdateCount1 part (equivalent) behind the scenes. And that only works when there is a callback parameter with the right name: CountChanged.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论