Reputation: 32996
In my Rating.razor.cs component I have:
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
Based on replies from individuals here I respect, a parameter should never have a value assigned to it inside the component. Update per @HH below - The Value
is only set by the parent razor file.
And the way for the parent component to get the changed value is by assigning a method to ValueChanged
- correct? So the component should have an internal property that stores the new value if it needs to retain the present value - correct?
And... @bind-Value=Model.Rating
is a shortcut for:
<Rating Value="Model.Rating" ValueChanged="BlazorCreatedOnValueChanged"/>
private void BlazorCreatedOnValueChanged(int rating)
{
Model.Rating = rating;
}
Is this correct?
Based on the answers & comments below (as always thank you Bennyboy, HH, & MrC)
First off, read the answers & comments below in full. I'm summarizing here and there's details in the below answers that are not included here.
The way you should set this up, using rating as an example
Value=Model.Rating
.ValueChanged(rating)
passing the new rating to the parent.Model.Rating = rating;
Value=Model.Rating
and renders using this new value.Notes:
Value
.@bind-value
and bind:set/get
are shorthand to the above cycle. To the extent that for the set it creates it's own ValueChanged method in the parent razor to call to update the Model.Rating
.Value
is updated and that updated value is used to display the child. But it is updated via the cycle above that triggers a render that causes the Value
to be read anew.Upvotes: 0
Views: 797
Reputation: 273621
So does this mean Value is the initial vale for my rating component?
No, of course not.
When this component executes ValueChanged.InvokeAsync(newValue) then the parent component will register that (and maybe validate/limit the value) and then set a new Value with the other branch of the two-way binding. Value always is what the parent says it is.
The "don't write to your own parameters" rule is there to avoid having two conflicting versions of the truth. In this example Model.Rating is leading.
It is a bit confusing because the data-flow takes the long way around:
newValue -> ValueChanged -> Model.Rating -> Render -> OnSetParameters -> Value
Upvotes: 1
Reputation: 30320
Here, it's vitally important to understand the underlying processes.
Edit events are all driven from the Browser event. The Blazor Client side JS code has a registered handler for the event which calls back though JS Interop into the Hub/Renderer process. This invokes the mapped C# method: in this case that's ValueChanged
.
There's no magic "automatic" setting of Value
. Value
remains at the old value until the parent sets the incoming parameter value, the renderer calls SetParametersAsync
on the component, and the ParameterView.SetParameterProperties
sets it to the new [Parameter supplied] value.
This process isn't without its problems.
SetParametersAsync
, the component will be rendered using the current [not new] value of Value
.Even Microsoft resorts to a little skulduggery in InputBase
to avert this. The code does a small NoNo by temporarily setting Value
locally. There's an explanation why attached to the offending line.
protected TValue? CurrentValue
{
get => Value;
set
{
var hasChanged = !EqualityComparer<TValue>.Default.Equals(value, Value);
if (hasChanged)
{
_parsingFailed = false;
// If we don't do this, then when the user edits from A to B, we'd:
// - Do a render that changes back to A
// - Then send the updated value to the parent, which sends the B back to this component
// - Do another render that changes it to B again
// The unnecessary reversion from B to A can cause selection to be lost while typing
// A better solution would be somehow forcing the parent component's render to occur first,
// but that would involve a complex change in the renderer to keep the render queue sorted
// by component depth or similar.
Value = value;
_ = ValueChanged.InvokeAsync(Value);
EditContext?.NotifyFieldChanged(FieldIdentifier);
}
}
}
Upvotes: 1
Reputation: 374
[Parameter] Val in Rating set by parent -> represent current rating; ValueChanged lets parent react to changes.
@bind-Value=Model.Rating binds child rating to parent's Model.Rating, auto- updating; BlazorCreatedOnValueChanged updates parent when rating changes.
Upvotes: 0