How can I retrieve multiple checkbox values from checkboxes added dynamically in Blazor when using EditForm and/or @bind-Value?

Viewed 50

I am trying to find out how to get checkbox values when checked in a form in Blazor.

I can see from this post (.NET Core Blazor: How to get the Checkbox value if it is checked? ), how to do it for a single value:

a solution to the above post gave me something like this:

<InputCheckbox @bind-Value="@b.myBool" checked="@(@b.myBool?"checked":null)" />CheckMe

and that works ok. I just create a class with a boolean property (say myBool) and declare an instance of the class with that property set to false. Then when the form is submitted, I have access to b.myBool, it is changed by the user.

However, I need to do it for multiple Checkboxes that will be added dynamically (the text next to the checkbox, here CheckMe[z], would also change but that's not an issue).

So, I figured a for/foreach loop with something like:

<InputCheckbox @bind-Value="@b.myBool[i]" checked="@(@b.myBool[i]?"checked":null)" />CheckMe[z]

and in the class, I'd just need to replace with a list property e.g.: List myBools.

Unfortunately this and lots of other variations I've tried don't work, for many many different reasons.

Thanks in advance for advice/suggestions/links.

Edit to my question (thanks Lex).

The latest error I receive is: "ArgumentException: The provided expression contains a InstanceMethodCallExpression1 which is not supported. FieldIdentifier only supports simple member accessors (fields, properties) of an object."

That is after trying trying:

**<InputCheckbox @bind-Value="@e.myBools[i]" checked="@(@e.myBools[i]?"checked":null)" />**

and the @code:

**public class editFormModel
{
public List<bool> myBools { get; set; }
}
editFormModel e = new() {
myBools = new List<bool> { true, true, true, true},
};**

nb the angle brackets seem to disappear in this post but I hope it's clear what I mean.

Additional Thanks for your answers.

Following on from that, I really want now to include an array of Values per Option and then one checkbox for each. So I figured just a nested loop and I could just adjust the EditFormModel to allow for a bool[] Values array. I tried with the below code and got the error:

ArgumentException: The provided expression contains a SimpleBinaryExpression which is not supported. FieldIdentifier only supports simple member accessors (fields, properties) of an object.

Any help would again be much appreciated. Apologies for not explaining entire task at hand from beginning but wasnt sure how far I would get.

@page "/counter"

<PageTitle>Counter</PageTitle>

<div>
    <EditForm Model="this.e">
        @for (int i = 0; i < this.e.Options.Count; i++)
        {
                {
                    <div>
                        @for (int j = 0; j < 1; j++)
                        {
                            <label>
                                <InputCheckbox @bind-` 
  `Value="this.e.Options[i].Values[j] " />
                                @*@option.Name*@
                            </label>
                        }
                    </div>
                }
            }
    </EditForm>
</div>
<div>

 Gives access to results like:
     @e.Options[1].Values[0] 
    @e.Options[1].Values[1] 
    @e.Options[1].Values[0] 
    @e.Options[1].Values[1] 

</div>

@code
{
    public class EditFormModel
    {
        public List<Option> Options { get; set; }
    }

    public class Option
    {
        public string? Name { get; set; }

        public bool[] Values { get; set; }
    }


    public EditFormModel e = new()
    {
        Options = new List<Option>
        {
            new()
            {
                Name = "Option 1",
                Values = new bool[] {
                    true, true
                }
            },
            new()
            {
                Name = "Option 2",
                Values = new bool[] {
                    true, true,
                }
            }
        }
    };
}
2 Answers

Here's some code that should work - I just tried it in a small sample app. It's very contrived, but it does illustrate the general approach that you're using should be fine in theory.

Test.razor

@page "/test"

<div>
    <EditForm Model="this.e">
        @foreach (var option in this.e.Options)
        {
            <div>
                <label>
                    <InputCheckbox @bind-Value="option.Value" />
                    @option.Name
                </label>
            </div>
        }
    </EditForm>
</div>
<div>
    Options selected: @this.e.Options.Count(x => x.Value)
</div>

@code
{
    public class EditFormModel
    {
        public List<Option> Options { get; set; }
    }

    public class Option
    {
        public string Name { get; set; }

        public bool Value { get; set; }
    }

    private EditFormModel e = new()
        {
            Options = new List<Option>
                {
                    new()
                        {
                            Name = "Option 1",
                            Value = true,
                        },
                    new()
                        {
                            Name = "Option 2",
                            Value = false,
                        },
                    new()
                        {
                            Name = "Option 3",
                            Value = false,
                        },
                    new()
                        {
                            Name = "Option 4",
                            Value = true,
                        },
                },
        };
}

According to these posts - [1],[2], you can't use an index to bind to a collection of primitive values because Blazor will have trouble tracking these values in EditContext. An index is, apparently, not a reliable tracker because a collection may be modified in various ways where an object at the beginning of the collection could end up elsewhere or be deleted.

So in order for this to work, you will need to create an object e.g.

public class CheckTracker{
    public bool IsChecked {get;set;}
}

and change your collection to -

public class EditFormModel
{
    public List<CheckTracker> Trackers { get; set; }
}

EditFormModel e = new() {
   Trackers = new List<CheckTracker> { new CheckTracker{IsChecked:true}, new CheckTracker{IsChecked:true}, new CheckTracker{IsChecked:true}, new CheckTracker{IsChecked:true}}
};

Finally, your binding should look like this -

<InputCheckbox @bind-Value="@e.Trackers[i].IsChecked" checked="@(@e.Trackers[i].IsChecked? "checked" :null)" />
Related