I've written a generic grid component where I've a checkbox in column header which can be selected to check all rows checkboxes and it working fine but there is an issue when I'm unselecting any other checkboxes then the main checkbox should also be unselected and that is not working as expected:
below is my code for the generic grid-
@if (Items == null)
{
<p><em>Loading...</em></p>
}
else {
<table >
<thead>
<tr>
<th>
<input type="checkbox" id="chkAll" checked="@SelectMain" @onchange="ChangeCheckboxState"/>
</th>
@Columns(default(TItem))
</tr>
</thead>
<tbody>
@foreach (var item in Items) {
var id = $"chk{counter}";
<CascadingValue Value="item">
<tr>
<td >
<input type="checkbox" id="@id" checked="@SelectAll" @onchange="ChangeCheckboxSingle"/>
</td>
@Columns(item)
</tr>
</CascadingValue>
counter ;
}
</tbody>
</table>
}
@code {
[Parameter]
public IList<TItem> Items { get; set; }
[Parameter]
public RenderFragment<TItem>? Columns { get; set; }
public bool SelectAll { get; set; } = false;
public bool SelectMain { get; set; } = false;
int counter = 1;
void ChangeCheckboxState(ChangeEventArgs e){
if (Convert.ToBoolean(e.Value))
SelectAll = true;
else
SelectAll = false;
}
void ChangeCheckboxSingle(ChangeEventArgs e){
SelectMain = false;
StateHasChanged();
}
}
and lastly, what is the actual difference between @onselectionchange and @onchange.
CodePudding user response:
Don't worry, I too find binding checkboxes quite annoying in Blazor. In short, your Render DOM gets out of sync with the Browser DOM. Find Shauns detailed explanation about what's happening here.
The trick is to toggle the set value using await Task.Yield();
between toggles. This allows Blazor to perform it's double render. Change your event handlers to this and it will work;
Note: your ChangeCheckboxState
handler could have been simplified to one line SelectAll = Convert.ToBoolean(e.Value)
. Below I just simply your event and toggle it using the yield trick.
async void ChangeCheckboxState(ChangeEventArgs e){
SelectAll = !Convert.ToBoolean(e.Value);
await Task.Yield();
SelectAll = !SelectAll;
StateHasChanged();
}
async void ChangeCheckboxSingle(ChangeEventArgs e){
SelectMain = true;
await Task.Yield();
SelectMain = false;
StateHasChanged();
}