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?
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 a Func
delegate that defines the actual model object/property. It's used internally to create a FieldIdentifier
object, which is used to identify the property in the EditContext
and ValidationStore
.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