skip to Main Content

So I’m having to ask for help here now after spending the past few nights trying everything and nothing working. This should be so simple. Basically I’m trying to get a value from a checkbox using dotnet 8. The problem is that nothing happens at all when I click the checkbox, after lots of Googling and particularly [this]Blazor Checkbox two-way binding and change event link on StackOverflow. I did note that dotnet 8 has a new [SupplyParameterFromForm] attribute that worked for something else that was always null but not for this.

@page "/Admin/Roles/Update/{Id}"
@layout AdminLayout
@using Microsoft.AspNetCore.Http.Extensions
@using System.Text
@using PersonalWebsiteBlazor.Models
@inject UserManager<AppUser> _userManager
@inject RoleManager<IdentityRole> _roleManager

<h2 class="bg-info p-1">Update Role</h2>
<NavLink class="btn btn-secondary" href="/Admin/Roles" Match="@NavLinkMatch.All">Back</NavLink>

@Message



<EditForm Model="this" FormName="UpdateRole" OnSubmit="HandleSubmit">
    <AntiforgeryToken />
    <input type="hidden" name="roleName" value="@RoleName" />
    <input type="hidden" name="roleId" value="@RoleId" />

    <h2 class="bg-info p-1 text-white">Add To @RoleName</h2>
    <table class="table table-bordered table-sm">
        @if (NonMembers.Count() == 0)
        {
            <tr><td colspan="2">All Users Are Members</td></tr>
        }
        else
        {
            @foreach (AppUser user in NonMembers)
            {
                <tr>
                    <td>@user.UserName</td>
                    <td>
                        <input type="checkbox" value="@user.Id" checked="@IsChecked" @onchange="CheckboxChecked" />
                    </td>
                </tr>
            }
        }
    </table>

    <h2 class="bg-info p-1 text-white">Remove From @RoleName</h2>
    <table class="table table-bordered table-sm">
        @if (Members.Count() == 0)
        {
            <tr><td colspan="2">No Users Are Members</td></tr>
        }
        else
        {
            @foreach (AppUser user in Members)
            {
                @if (user is not null)
                {
                    <tr>
                        <td>@user.UserName</td>
                        <td>
                            <input type="checkbox" name="DeleteIds" value="@user.Id" />
                        </td>


                    </tr>
                }

            }
        }
    </table>
    <button type="submit" class="btn btn-primary">Save</button>
</EditForm>




@code {
    public string? Message { get; set; } = "Hello World!";

    [Parameter]
    public bool IsChecked { get; set; } = false;

    private void CheckboxChecked()
    {
        Console.WriteLine(DateTime.Now);
    }

    [Parameter]
    public string Id { get; set; } = default!;

    public string? RoleName { get; set; }
    public string? RoleId { get; set; }

    public List<string> AddIds { get; set; } = new List<string>();

    public List<string> DeleteIds { get; set; } = new List<string>();

    public List<AppUser> Members { get; set; } = new List<AppUser>();
    public List<AppUser> NonMembers { get; set; } = new List<AppUser>();
    public IdentityRole? Role { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        IdentityRole? role = await _roleManager.FindByIdAsync(Id);
        List<AppUser> members = new List<AppUser>();
        List<AppUser> nonMembers = new List<AppUser>();
        foreach (AppUser user in _userManager.Users)
        {
            var list = await _userManager.IsInRoleAsync(user, role!.Name!) ? members : nonMembers;
            list.Add(user);
        }
        Role = role;
        Members = members;
        NonMembers = nonMembers;
    }

    public async Task HandleSubmit()
    {
        StringBuilder sb = new StringBuilder();
        foreach (var item in AddIds)
        {
            sb.Append(item);
        }
        Message = sb.ToString();
        await Task.CompletedTask;
    }
}

Relevant bit of code:

 @foreach (AppUser user in NonMembers)
{
    <tr>
        <td>@user.UserName</td>
        <td>
            <input type="checkbox" value="@user.Id" checked="@IsChecked" @onchange="CheckboxChecked" />
        </td>
    </tr>
}

[Parameter]
public bool IsChecked { get; set; } = false;

private void CheckboxChecked()
{
    Console.WriteLine(DateTime.Now);
}

I’m just trying really simple things to just get a value from the checkbox right now.
Basically my question is why is it always null or empty and what is the solution???

Thank you

Douglas

2

Answers


  1. Different from .net7, you need specify server/webassembly interactive render mode to enable interactives https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0#render-modes.
    For Blazor server project,you could try add @rendermode InteractiveServer or @rendermode @(new InteractiveServerRenderMode(false)) at top of the razor components. Or you could also specify global render mode in app.razor.
    If you haven’t specify rendermode any where, it will be "Static server-side rendering" (static SSR). Functions won’t work.
    Besides, to "to get a value from a checkbox", you could try pass the userid as a string parameter to the CheckboxChecked function.

    @page "/"
    @rendermode InteractiveServer
    
    CheckBox A
    <input type="checkbox" @onchange="@((ChangeEventArgs e)=>CheckboxChecked(e,"A"))" />
    <br />
    CheckBox B
    <input type="checkbox" @onchange="@((ChangeEventArgs e)=>CheckboxChecked(e,"B"))" />
    
    @code{
        private void CheckboxChecked( ChangeEventArgs e,string boxName)
        {
            Console.WriteLine(boxName+" selected: "+e.Value);
        }
    }
    

    Test
    enter image description here

    Login or Signup to reply.
  2. At the moment you have one value and event handler for an array of checkboxes.

    I would recommend building a form model that you can use to bind to your form. You can then retrieve this state when the form is submitted. If you use a model that represents your form, each form value is represented by only one object property.

    Below is a simplified version of your scenario to demonstrate the concept. I have a link to a full working example of your scenario at the end.

    html

    <EditForm Model="@Model" OnSubmit="@OnSubmit">
      @foreach (var row in Model.Rows)
      {
        @*Two-way bind the checkbox value here.*@
        <input type="checkbox" @bind-value="@row.Checked" />
      }
      <button type="submit">Submit</button>
    </EditForm>
    

    code

    @code {
      // TODO: declare classes in dedicated files
      public class FormModel
      {
        public List<FormRowModel> Rows {get; set;} = new();
      }
    
      public class FormRowModel
      {
        public bool Checked {get; set;}
      }
    
      // declare form model property to bind to
      private FormModel Model {get; set;} = new();
    
      protected override void OnInitialized()
      {
        // build the model - probably from your database
        Model = new FormModel
        {
          Rows = new List<FormRowModel>
          {
            new FormRowModel(),
            new FormRowModel()
          }
        }
      }
    
      private void HandleSubmit()
      {
        var checked = Model
          .Rows
          .Where(x => x.Checked);
    
        // TODO: persist the updated state to the database
      }
    }
    

    The main thing to note here is that I am using @bind-value on the checkboxes to perform two-way binding between the checkbox and the model. You can then access this bound state at any point – crucially at the point that the form is submitted.

    Full working example: https://blazorfiddle.com/s/aihbmdqt

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search