When to use ValueChanged and ValueExpression in Blazor?

I would like to add a few use cases for ValueChanged and ValueExpression,

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 e.g. @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.

e.g. from 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).

Use cases

1 – Creating a component that wraps another component with @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.

e.g.

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. 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

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.

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. This will make your component have the default value and will also change any property in @bind-Value to have the default value.

e.g.

// 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

When you have an nullable type, e.g. 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. 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.

Leave a Comment