Aspnetcore: Nested binding using @bind-xxx syntax does not work

Created on 28 Jun 2019  路  9Comments  路  Source: dotnet/aspnetcore

Describe the bug

Binding values using @bind-XXX binding syntax does not work if nested.

Demo

BlazorBug04

To Reproduce

Steps to reproduce the behavior:

  1. Create a Blazor client-side application using Preview6
  2. Create a new custom control that also uses @bind-Value syntax in the Shared folder
  3. Create an EditForm in the Index.razor page and a model to bind to
  4. Create an InputText component, bind using @bind-Value to one model property
  5. Create a custom control instance using @bind-Value

Expected behavior

Expected the binding to work with nested controls, so both properties of the model are updated when the input control is changed.

Actual behavior

A direct binding with InputText works but the custom control binding does not bind to the model. It binds when initially created but updates to the control do not get passed back the to page model.

Sample code

Example repo created at https://github.com/conficient/BlazorBug04

MyControl.razor

<div class="form-control">
    <label>@Label</label>
    <InputText @bind-Value="@Value" />
</div>

@code {
    // binding parameters

    [Parameter] public string Value { get; private set; }

    [Parameter] public EventCallback<string> ValueChanged { get; private set; }

    [Parameter] public string Label { get; private set; }
}

Index.razor

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<EditForm Model="@model">
    A: <InputText @bind-Value="@model.A" /><br />
    <MyControl @bind-Value="@model.B" Label="B" />
</EditForm>

<b>Binding check:</b>
<p>A = @model.A</p>
<p>B = @model.B</p>
@code
{
/// <summary>
/// Data class
/// </summary>
public class Model
{
    public string A { get; set; }
    public string B { get; set; }
}

private Model model = new Model()
{
    A = "initial A",
    B = "intial B"
};
}

Additional context

Previously reported on Blazor repo: #1490
Also reported in aspnetcore repo: #5504
Related: #6351 was supposed to fix?

__Include the output of dotnet --info__

.NET Core SDK (reflecting any global.json):
 Version:   3.0.100-preview6-012264
 Commit:    be3f0c1a03

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17763
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.0.100-preview6-012264\

Host (useful for support):
  Version: 3.0.0-preview6-27804-01
  Commit:  fdf81c6faf

.NET Core SDKs installed:
  2.1.202 [C:\Program Files\dotnet\sdk]
  2.1.401 [C:\Program Files\dotnet\sdk]
  2.1.505 [C:\Program Files\dotnet\sdk]
  2.1.604 [C:\Program Files\dotnet\sdk]
  2.1.700-preview-009618 [C:\Program Files\dotnet\sdk]
  2.1.700 [C:\Program Files\dotnet\sdk]
  2.1.800-preview-009677 [C:\Program Files\dotnet\sdk]
  2.1.800-preview-009696 [C:\Program Files\dotnet\sdk]
  2.2.103 [C:\Program Files\dotnet\sdk]
  3.0.100-preview6-012264 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview6.19307.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview6-27804-01 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview6-27804-01 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download
area-blazor question

All 9 comments

Thanks for contacting us, @conficient .
You can write it this way, and the changes would propagate.

<InputText Value="@Value" ValueChanged="@ValueChanged" />

Alternatively, you can simply subclass from the InputText component.

I tried the approach you suggested but this results in a WASM error:

WASM: System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputText requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'

I think this is because the ValueChanged event handler isn't set?
I resolved my issue anyway once I realised (thanks to @joshlang ) that I needed to invoke the ValueChanged from the Value property setter.

I will push the updated version on this code to my repo. This issue can remain closed though.

If your goal is to wrap up an InputText in some bootstrap styling, I would suggest subclassing. The form types are designed for this use case.

If you're really determined to compose, then accept an Expression<Func<string>> ValueExpression as a parameter as well and pass that through also.

Thanks @rynowak - yes I wanted to get rid of boilerplate <div class="form-group"><label>.... around each input control so my forms are simpler. I'll try what you suggest.

I started to see the following error after upgrading from preview5 to preview6

WASM: System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputText requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'

this is my custom component
_the razor file_

@inherits CcInputTextComponent
@using Microsoft.AspNetCore.Components
<input type="@Type" 
       class="@CssClass"
       id="@Id"
       value="@BindMethods.GetValue(CurrentValue)" 
       onchange="@BindMethods.SetValueHandler(__value => CurrentValue = __value, CurrentValue)"
       placeholder="@PlaceHolder"
/>

_the "code behind" - I prefer not to use the @ code{} I like the idea of having the csharp completely separated from the razor_

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.RenderTree;

namespace BzTest.Shared.CcInputText
{
    public class CcInputTextComponent: InputText
    {
        [Parameter] public string Type { get; set; } = "text";

        [Parameter] public string PlaceHolder { get; set; }

    }
}

_the use of the component_

<CcInputText id="Password" Type="password" bind-Value="@loginRequest.Password" Class="form-control input_pass" PlaceHolder="Password"></CcInputText>

use @bind-Value instead of bind-Value. See the preview6 upgrade notes for more details

Yep, that seems to work. I get a red squiggle but might be Resharper's fault.

image

I don't know looks weird, not the squiggle, the syntax. The @ before the attribute name, I don't know it's different from the rest of the Razor syntax, maybe just have to get used to.

For those that end up here like me, it appears as though "@bind-Value" became case sensitive at some point.

Yes it did. Which now allows us to create components like

Was this page helpful?
0 / 5 - 0 ratings

Related issues

reduckted picture reduckted  路  91Comments

KerolosMalak picture KerolosMalak  路  269Comments

kevinchalet picture kevinchalet  路  761Comments

glennc picture glennc  路  117Comments

radenkozec picture radenkozec  路  114Comments