英文:
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
:
Value
is the "in" value for the control - it's strongly typed.ValueChanged
is the "out" value for the control: A callback with a strongly typed value.ValueExpression
is aFunc
delegate that defines the actual model object/property. It's used internally to create aFieldIdentifier
object, which is used to identify the property in theEditContext
andValidationStore
.
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论