2

I am loading data to a grid with 3 columns in Blazor. For the grid structure I have used Bootstrap rows and columns. If I need to highlight rows on hover and keep the row selected using onClick event what is the best way to achieve it?

Below is how I populate data.

<div class="container-fluid">
    <div class="row">
        <div class="col">User Name</div>
        <div class="col">First Name</div>
        <div class="col">Last Name</div>
    </div>

    @foreach (var user in users)
    {
        <div class="row">
            <div class="col">@user.UserName</div>
            <div class="col">@user.FirstName</div>
            <div class="col">@user.LastName</div>
        </div>
    }
</div>

Thanks.

ZionSaver
  • 33
  • 1
  • 4

4 Answers4

5

You need to maintain the rows selected state. Here is a generic view model to wrap any row type:

public class RowState<TItem>
{
    public RowState(TItem item) => Item = item;
    public TItem Item { get; set; }
    public bool IsSelected { get; set; }
}

Then using style classes for both selected and hover:

.hover-row:hover {
    background-color: #0000ff33;
}

.selected-row {
    background-color: #0000ff77;
}

I seperated the row into its own component UserRow.razor with a [Parameter] that takes the RowState<User> viewmodel.

This simplifies the logic to handle the click event and logic for the css class to highlight the row.

UserRow.razor

<div class="row hover-row @CssClass" @onclick="RowClicked" >
    <div class="col">@User.UserName</div>
    <div class="col">@User.FirstName</div>
    <div class="col">@User.LastName</div>
</div>

@code {
    [Parameter]
    public RowState<User> UserRowState { get; set; }   

    private void RowClicked() => UserRowState.IsSelected = !UserRowState.IsSelected;

    private User User => UserRowState.Item;
    private bool IsSelected => UserRowState.IsSelected;
    private string CssClass => IsSelected ? "selected-row" : "";
}

Refactoring your original code:

<div class="container-fluid">
    <div class="row">
        <div class="col">User Name</div>
        <div class="col">First Name</div>
        <div class="col">Last Name</div>
    </div>

    @foreach (var user in UserRows)
    {
        <UserRow UserRowState=user />     
    }
</div>

@code {
    IEnumerable<RowState<User>> UserRows = User.DemoData
        .Select(user => new RowState<User>(user))
        .ToList();
}

Here is a working REPL

Brian Parker
  • 11,946
  • 2
  • 31
  • 41
  • Thank you @BrianParker that worked well for me. – ZionSaver Oct 06 '21 at 01:23
  • I was using the onmouseover and onmouse out method. however whenever you crossed table grid lines this would cause the event to re-fire. by using css instead mentioned here on the tr property this removed that behaviour and solved this issue. – Mitch Storrie Jun 05 '22 at 02:33
2
<table class="table table-hover">
<tr>
    <td >User Name</td>
    <td >First Name</td>
    <td >Last Name</td>
</tr>

@foreach (var user in users)
{
    string bg = (user.Id == selectedUserId)? "bg-info" : "";
    <tr class=@bg @onclick=@SelectUser(user.Id) @key=user>
        <td >@user.UserName</td>
        <td >@user.FirstName</td>
        <td >@user.LastName</td>
    </tr>
}
@code{

  private int selectedUserId {get;set;} = 0;
  private void SelectUser(int userId){
   selectedUserId = userId;
  }

}
Ali Borjian
  • 941
  • 10
  • 18
  • Code only answers go against community standards. – Arleigh Hix Sep 16 '21 at 04:18
  • please tell me the problem, I want to learn. – Ali Borjian Sep 16 '21 at 04:23
  • We all want to learn, that is the problem. You are supposed to explain *how* this answer solves the problem, and link to supporting documentation where it is appropriate so that others may learn from it. I personally do not know anything about Blazor, and this teaches me nothing. – Arleigh Hix Sep 16 '21 at 04:32
  • Dear friend, it is really surprising that when you have no information about something you try to guide others. I think it is better for others to judge our answers. – Ali Borjian Sep 16 '21 at 04:37
  • @AliBorjian Thank you for your answer. However, you are using the table structure which I am not using in my question. Also it is something that I am moving away and will not suit the style of my responsive application. – ZionSaver Sep 17 '21 at 05:59
  • Yes, this is answer is perfectly fine if you only want to select a single user. @ZionSaver, it doesn't matter whether the answer uses a table; that's just formatting, it has nothing to do with how to select a row. You can use a div, a span, a table row, a paragraph-- whatever you want. – Bennyboy1973 Sep 18 '21 at 10:56
0

None of the JavaScript answers is appropriate for Blazor. Ignore them.

If you just want to select a single row, you can use @Ali Borjian's answer

In order to track selection of multiple rows, you can use one of two methods:

  1. Use a class that tracks state information per row. This is Brian's answer.
  2. Create a secondary list that tracks which rows are selected. This follows.

.

<div class="container-fluid">
    <div class="row">
        <div class="col">User ID</div>
        <div class="col">User Name</div>
    </div>

    @foreach (var user in Users)
    {
        string CSS = SelectedUsers.Contains(user) ? " Selected" : "";
        <div class="row @CSS" style="" @onclick="()=>ToggleSelection(user)">
            <div class="col">@user.ID</div>
            <div class="col">@user.Name</div>
        </div>
    }
</div>

@code {
    class User { public int ID; public string Name;  }

    List<User> Users = new List<User> { new User { ID = 1, Name = "Benjamin" }, new User { ID = 2, Name = "John" }, new User { ID = 3, Name = "David" } };
    List<User> SelectedUsers = new();

    void ToggleSelection(User user)
    {
        if (SelectedUsers.Any(u => u == user)) SelectedUsers.Remove(user);
        else SelectedUsers.Add(user);
    }

}
<style>
    .row {cursor:pointer}
    .row:hover { background-color:#c2d8ff }
    .Selected {background-color: #4781e9}
    .Selected:hover {background-color: #83aefb}
</style>
Bennyboy1973
  • 3,413
  • 2
  • 11
  • 16
-1

You can utilize Radio toggle buttons (for single selection) or Checkbox toggle buttons (for multiple) as Block buttons with just a little bit of custom styling. Put the whole user row inside the label and use the username or other unique field for the input id.

Here is an example using Radio toggle buttons:

label.btn {
  border: none;
  border-radius: 0;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">

<div class="container-fluid">
  <div class="row">
    <div class="col-4">User Name</div>
    <div class="col-4">First Name</div>
    <div class="col-4">Last Name</div>
  </div>

  <div class="row">
    <!-- @foreach (var user in users)
    { -->
    <div class="col-12 d-grid p-0">
      <input type="radio" class="btn-check" name="options" id="@user.UserName" autocomplete="off">
      <label class="btn btn-outline-dark text-start" for="@user.UserName">
        <div class="row">
          <div class="col-4">@user.UserName</div>
          <div class="col-4">@user.FirstName</div>
          <div class="col-4">@user.LastName</div>
        </div>
      </label>
    </div>
    <!-- } -->

    <div class="col-12 d-grid p-0">
      <input type="radio" class="btn-check" name="options" id="@user.UserName2" autocomplete="off">
      <label class="btn btn-outline-dark text-start" for="@user.UserName2">
        <div class="row">
          <div class="col-4">@user.UserName2</div>
          <div class="col-4">@user.FirstName</div>
          <div class="col-4">@user.LastName</div>
        </div>
      </label>
    </div>
    <div class="col-12 d-grid p-0">
      <input type="radio" class="btn-check" name="options" id="@user.UserName3" autocomplete="off">
      <label class="btn btn-outline-dark text-start" for="@user.UserName3">
        <div class="row">
          <div class="col-4">@user.UserName3</div>
          <div class="col-4">@user.FirstName</div>
          <div class="col-4">@user.LastName</div>
        </div>
      </label>
    </div>

  </div>
</div>
Arleigh Hix
  • 9,990
  • 1
  • 14
  • 31