j.xiang
j.xiang

Reputation: 73

Blazor - How to bind list of classes to input

In the //this works, the item.isSelected is updated but the set in private List<CheckboxModel> checkboxes won't run. And in the //this causes infinite loop in this component, nothing is updated and the foreach loop keeps running infinitely.

        @foreach (var item in checkboxes)
        {
            //this works 
            @*<div class="checkbox" @key="item">
                <input type="checkbox" id="@item.text" @bind="item.isSelected">
                <label for="@item.text">@item.text</label>
            </div>*@
            Console.WriteLine(item.text);
            //this causes infinite loop in this component
            <Checkbox id="@item.text" @key="item" label="@item.text" @bind-isSelected="item.isSelected" />
        }
    private List<CheckboxModel> _checkboxes { get; set; } = new List<CheckboxModel>();

    private List<CheckboxModel> checkboxes
    {
        get => _checkboxes;
        set
        {
            
            // won't run
            _checkboxes = value;
            Console.WriteLine(string.Join(',', value.Where(x => x.isSelected).Select(x => x.text)));
        }
    }

This is CheckboxModel.cs

    public class CheckboxModel
    {
        public bool isSelected { get; set; }
        public string text { get; set; }
    }

Checkbox.razor

<div class="checkbox">
    <input type="checkbox" id="@id" @bind="@isSelected">
    <label for="@id">@label</label>
</div>
@code {

    [Parameter]
    public string id { get; set; }

    [Parameter]
    public string label { get; set; }

    private bool _isSelected { get; set; }
    [Parameter]
    public bool isSelected
    {
        get => _isSelected;
        set
        {
            isSelectedChanged.InvokeAsync(value);
        }
    }

    [Parameter]
    public EventCallback<bool> isSelectedChanged { get; set; }

}

Upvotes: 0

Views: 2156

Answers (1)

j.xiang
j.xiang

Reputation: 73

What I end up doing was changing Checkbox.razor to CheckboxList.razor

@foreach (var item in items)
{
    <div class="checkbox" @key="item">
        <input type="checkbox" id="@item.text" checked="@item.isSelected" @onchange="args=> checkedChanged(item,args)">
        <label for="@item.text">@item.text</label>
    </div>
}

@code {
    [Parameter]
    public List<CheckboxModel> items { get; set; }
    [Parameter]
    public EventCallback<List<CheckboxModel>> itemsChanged { get; set; }

    async Task checkedChanged(CheckboxModel item, ChangeEventArgs args)
    {
        item.isSelected = (bool)args.Value;
        await itemsChanged.InvokeAsync(items);
    }
}

and in the parent file

<CheckboxList @bind-items="checkboxes" />
@code {
    private List<CheckboxModel> checkboxes { get; set; }
}

You can find 2 links in here. One of them is simpler and the other one has a select all button

In short: I let the child component handle the logic of looping and handling the List passed into it. The child component passes the List back instead of just a bool which fixes the problem where it doesn't trigger the setter function when I update an item in a List.

Upvotes: 2

Related Questions