I have a class MealsQueryInputs
that I would like to use as a component parameter with two-way binding capabilities.
All of the demos and sample code I can find are using built-in primitive types and never a class. I can get the MS demos to work but I cannot get binding to a class to work. Is it even possible to do this?
My component FilterSortOptions.razor
:
using WhatIsForDinner.Shared.Models
<MudCheckBox Checked="@QueryInputs.Favorite"
Color="Color.Inherit"
CheckedIcon="@Icons.Material.Filled.Favorite"
UncheckedIcon="@Icons.Material.Filled.FavoriteBorder"
T="bool"/>
<MudRating SelectedValue="@QueryInputs.Rating"/>
<MudButton OnClick="@(async () => await OnPropertyChanged())">Apply</MudButton>
@code {
[Parameter]
public MealsQueryInputs QueryInputs { get; set; }
[Parameter]
public EventCallback<MealsQueryInputs> QueryInputsChanged { get; set; }
private async Task OnPropertyChanged()
{
await QueryInputsChanged.InvokeAsync(QueryInputs);
}
}
CodePudding user response:
There are a couple of issues here:
QueryInputs
is aParameter
and therefore should never be modified by the code within the component. You end up with a mismatch between what the Renderer thinks the value is and what it actually is.When the parent component renders it will always cause a re-render of any component that is passed a class as a parameter. The Renderer has no way of telling if a class has been modified, so it applies the heavy handed solution - call
SetParametersAsync
on the component.
A solution is to use a view service to hold the data and events to notify changes. One version of the truth! Search "Blazor Notification Pattern" for examples of how to implement this. I'll post some code if you can't find what you want.
CodePudding user response:
As MrC said, you should avoid directly binding to the data being supplied as a parameter.
Here is a simple working sample (not MudBlazor) to show the concept
https://blazorrepl.telerik.com/QQEnQjaO54LY3MYK35
You bind to a local variable/property and try not to modify the incoming data directly.
MyComponent
<h1>MyComponent</h1>
<label for="choice">Choose</label>
<input id="choice" type="checkbox" @bind-value=localValue />
@code
{
bool localValue
{
get => Data.SomeChoice;
set {
if (value != localValue)
{
localData = Data with { SomeChoice = value };
InvokeAsync(ValueChanged);
}
}
}
ComplexObject localData;
[Parameter] public ComplexObject Data { get; set; }
[Parameter] public EventCallback<ComplexObject> DataChanged { get; set; }
Task ValueChanged() => DataChanged.InvokeAsync(localData);
}
ComplexObject
public record ComplexObject(bool SomeChoice, string SomeText);
Main
@code
{
ComplexObject data = new(false,"");
}
<MyComponent @bind-Data=data />
You have chosen @data.SomeChoice