@bind-Value in Blazor WASM

265 Views Asked by At

I'm trying to use nested two-way binding in Blazor, and I'm running into an issue where I need to bind an InputText value to a prop of an object. I have a model I pass to a custom form component and then to a single custom input text based on the prop type:

Index page

<div class="d-flex flex-column gap-3">
    <FormSection Forecast=@forecast />
    <button class="btn btn-sm btn-success" @onclick=SaveData>Save data</button>
</div>

FormComponent

<div>
    <label for="datetime" class="form-label">Date</label>
    <InputDateCustom TextValue="@Forecast.Date" />
</div>
<div>
    <label for="temperature" class="form-label">Temperature</label>
    <InputNumberCustom TextValue="@Forecast.TemperatureC" />
</div>
<div>
    <label for="summary" class="form-label">Summary</label>
    <InputTextCustom TextValue="@Forecast.Summary" />
</div>

@code {
    [Parameter] public WeatherForecast Forecast { get; set; }
}

Input date

<input class="form-control form-control-sm" type="date" @bind-value=TextValue />

@code {
    [Parameter] public DateTime TextValue { get; set; } = DateTime.Now;
}

Input text

<input class="form-control form-control-sm" type="text" @bind-value=TextValue />

@code {
    [Parameter] public string TextValue { get; set; } = string.Empty;
}

Input number

<input class="form-control form-control-sm" type="number" @bind-value=TextValue />

@code {
    [Parameter] public int TextValue { get; set; } = 0;
}

With this structure, when I save data with the button in the Index page there are no data in the model. If I use @bind-Value in the form component when I pass the value inside the input component (@bind-Value=Forecast.Date instead of TextValue="@Forecast.Date"), I get the error: Object of type 'TestProject.Client.Components.InputTextCustom' does not have a property matching the name 'TextValueChanged'

Instead, using the input components directly in the form component, it works and I see the values in the model.

What am I doing wrong? What's the best way to use two-way binding with nested components?

2

There are 2 best solutions below

2
Erik Kalström On

I think you need to Inherit from InputBase

0
MrC aka Shaun Curtis On

There are several issues with your implementation.

If you use input, you need to use bind, not bind-Value.

So your InputTextCustom needs to look like this:

<input class="form-control form-control-sm" type="text" @bind:get="Value" @bind:set="this.HasChanged"  />

@code {
    [Parameter] public string? Value { get; set; } = string.Empty;
    [Parameter] public EventCallback<string?> ValueChanged { get; set; }

    private Task HasChanged(string value)
    {
        return this.ValueChanged.InvokeAsync(value);
    }
}

And your FormComponent looks like this:

//...
<div>
    <label for="summary" class="form-label">Summary</label>
    <InputTextCustom @bind-Value="@Forecast.Summary" />
</div>

@code {
    [Parameter] public WeatherForecast Forecast { get; set; } = new();
}

My form then looks like this:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<FormComponent Forecast="_forecast" />
<div class="m-2 text-end">
    <button class="btn btn-primary" @onclick="Save">Save</button>
</div>

<div class="bg-dark text-white m-2 p-2">
    <pre>Summary: @_forecast.Summary</pre>
</div>

@code {
    private WeatherForecast _forecast = new();

    private void Save()
    {
        
    }
}

Note that this doesn't display the current value of Summary, although the state is correct. There's no built in mechanism for parents to see state changes in shared objects. How you handle this depends on your context.