Skip to content

Blazor bindings broken by cascading value #41700

@ericgrantholland

Description

@ericgrantholland

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Related to #11308. Bindings don't work properly (change behavior) when a component is surrounded by a CascadingParameter.
In my case, I am trying to wrap a third-party input in my own custom component (2 levels of binding 3rd party -> CustomTextBox -> Index.razor). You must have 2+ levels of chained binding to see the problem.

I'd like this to be reconsidered for a fix. The fact that adding a CascadingValue changes the way that bindings work is a framework bug in my opinion.

Take note of the code below with specific focus on the Index.razor page. I have 2 text box components that are nearly identical, except 1 is wrapped in a cascadingvalue tag, and another is not. Bindings work as expected for the component that is not wrapped, but notice you cannot set the value at all for the first text box (typing in the input appears to do nothing).

Index.razor

@page "/"

TextBox1Value: @TextBox1Value
TextBox2Value: @TextBox2Value

<CascadingValue Value="DateTime.Now">
    <CustomTextBox @bind-Value="@TextBox1Value" Placeholder="Not working textbox"></CustomTextBox>
</CascadingValue>
    
<CustomTextBox @bind-Value="@TextBox2Value" Placeholder="Working textbox"></CustomTextBox>


@code{

    string TextBox1Value { get; set; }
    string TextBox2Value { get; set; }
}

ThirdPartyComponent.razor

<input @bind-value="@Value" @bind-value:event="oninput" placeholder="@Placeholder"/>

@code {    
    string _value;
    [Parameter] public string Value
    { 
        get
        {
            return _value;
        }
        set
        {
            if(value != _value)
            {
                _value = value;
                ValueChanged.InvokeAsync(_value);
            }
        }
    }
    [Parameter] public EventCallback<string> ValueChanged { get; set; }
    [Parameter] public string Placeholder{get;set;}
}

CustomTextBox.razor

<ThirdPartyComponent @bind-Value="Value" Placeholder="@Placeholder"></ThirdPartyComponent>


@code {
    string _value;
    [Parameter] public string Value
    { 
        get
        {
            return _value;
        }
        set
        {
            if(value != _value)
            {
                _value = value;
                ValueChanged.InvokeAsync(_value);
            }
        }
    }
    [Parameter] public EventCallback<string> ValueChanged { get; set; }
    [Parameter] public string Placeholder{get;set;}
    [CascadingParameter] public DateTime MyCascadingDate{ get; set; }

}

Notice that the TextBox1Value is never set. This is because the setter is called twice, the first time with the correct newValue, and a second time with an incorrect original value (reverts new value to original). Thus, you can never change the value simply because it is wrapped by a CascadingValue.

I understand that Microsoft suggests not to do bindings this way, however, why do bindings work differently depending on whether they are wrapped by a cascading parameter?

Expected Behavior

Expect bindings to work the same, regardless of whether they are wrapped by a cascading value or not.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

dotnet6

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions