Here is the example code:
Parent.razor:
@page "/"
<ChildComponent @ref="Child"/>
<br/>
Parent Total: @(Child?.Total ?? 0)
@code {
public ChildComponent Child { get; set; }
}
ChildComponent.razor:
@for (int i = 0; i < 10; i++)
{
int i1 = i;
<input type="checkbox" @onchange="v => OnSelect((bool)v.Value, i1)"/>
}
<br/>
Child Total: @Total
@code {
public List<int> SelectedItems { get; set; } = new List<int>();
public int Total => SelectedItems.Sum();
public void OnSelect(bool selected, int index)
{
if (selected)
{
SelectedItems.Add(index);
}
else
{
SelectedItems.Remove(index);
}
}
}
(Blazor Fiddle: https://blazorfiddle.com/s/srmk6ehc)
Note that the @Total
in the child component gets updated automatically in response to the checkboxes being clicked or unclicked, but the @Child.Total
in the parent component never gets updated. How do I bind in such a way that the parent value will be redrawn when something occurs in the child component?
Note: This is just example code demonstrating the issue, please focus on the question of propagating updates from a child's readonly property, not other ways I could completely restructure the code.
Your example is somewhat contrived, but first a couple of points:
There are two commonly used mechanisms to communicate between components:
Flux
implementation.Here, I've used the Bind method [in a slightly contrived way].
What you don't see in the bind process is the code generated by the Razor compiler in the parent. The callback is handled as a UI event in the parent which triggers the ComponentBase
render process.
The component:
@for (int i = 0; i < 10; i++)
{
int i1 = i;
<input class="form-check-inline" type="checkbox" @onchange="v => OnSelect(v, i1)" />
}
<br />
Child Total: @Total
@code {
// Two Bind Parameters - we don't need the ValueExpression
[Parameter] public int Total { get; set; }
[Parameter] public EventCallback<int> TotalChanged { get; set; }
private List<int> SelectedItems { get; set; } = new List<int>();
public async Task OnSelect(ChangeEventArgs e, int index)
{
// Conversion here to keep the markup clean
var selected = (bool?)e.Value ?? false;
if (selected)
SelectedItems.Add(index);
else
SelectedItems.Remove(index);
// Call the eventcallback
await this.TotalChanged.InvokeAsync(this.SelectedItems.Count());
}
}
Demo Page:
@page "/"
<ChildComponent @bind-Total="_total" />
<div class="alert alert-primary">@_total</div>
@code {
private int _total;
}
The other answer shows how to use state objects. You can find other examples if you search on SO for "Blazor Notification Pattern".