locked
Blazor Table Input: How to Determine Which Cell Was Changed in a Table RRS feed

  • Question

  • User-343630552 posted

    I would like to validate the contents of table cells when the user makes changes, where the validation depends on which cell (row and column) is changed.  I don't want to make the table part of a form (if that's even possible) because of the "submit" requirement.  The problem is that the @oninput, @onkeydown, and @onchange events do not return the row and column indexes of the cell as far as I can tell, and none of the other events seem to help.  So, how can I pull this off?

    Thanks.  Steve

    Wednesday, September 9, 2020 2:39 PM

Answers

  • User-474980206 posted

    as you are using the html components <table>, <tr> and <td> they do not have row and column properties. you will need to pass:

    <td contenteditable="true" >
       <input @bind=@TableValue
             @oninput="@((ChangeEventArgs e) => BBDetail_OnInput(e, 1, 2))" />
    </td>

    private void BBDetail_OnInput(ChangeEventArgs e, int row, int col) { var x0 = e.Value; }

    of course you might use variable for the row/col rather than hard code. if you wrapped the input with a custom component, then you could have props for the role and col.

    your other option is to use javascript interop and calc the row col. if you had a unique id for the input, javascript could walk the dom, and calc the row and column, using the .parent and .sibling properties to count the td in tr (col), and tr in trbody (row)

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, September 9, 2020 6:15 PM
  • User475983607 posted

    Array example.

    @page "/tablecellevent"
    <h3>@message</h3>
    
    <table class="table">
        <thead>
            <tr>
                <th>Col 1</th>
                <th>Col 2</th>
                <th>Col 3</th>
                <th>Col 4</th>
                <th>Col 5</th>
            </tr>
        </thead>
        <tbody>
    
            @for (int row = 0; row < Grid.GetLength(0); row++)
            {
            <tr>
                @for (int col = 0; col < Grid.GetLength(1); col++)
                {
                    var ov = Grid[row, col];
                    var r = row;
                    var c = col;
                    <td>
                        <input type="text" 
                               name="@("Cell" + ov.ToString())" 
                               @onchange="@(e => OnUpdateGrid(e, r, c, ov))" 
                               value="@ov" />
                    </td>
    
                }
            </tr>
            }
        </tbody>
    </table>
    
    @code {
    
        private int[,] Grid;
        private string message = "Table Cell Event Tester";
    
        protected override void OnInitialized()
        {
    
            Grid = GetGrid();
        }
    
        private void OnUpdateGrid(ChangeEventArgs e, int row, int col, int originalValue)
        {
            string newValue = e.Value.ToString();
            if (newValue != originalValue.ToString())
            {
                message = $"You changed cell({row}, {col}) from {originalValue} to {e.Value}";
            }
    
            int[,] grid = Grid;
            grid[row, col] = int.Parse(newValue);
    
        }
    
    
        private int[,] GetGrid()
        {
            int[,] cells = new int[5, 5];
            int cell = 0;
            for (int row = 0; row < cells.GetLength(0); row++)
            {
                for (int col = 0; col < cells.GetLength(1); col++)
                {
                    cells[row, col] = cell++;
                }
            }
            return cells;
        }
    
    }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, September 9, 2020 7:22 PM

All replies

  • User475983607 posted

    It's hard to visualize the problem without code.  I assume a standard two dimensional array would solve this problem. 

    Wednesday, September 9, 2020 2:54 PM
  • User-474980206 posted

    The cell component should have row and column properties that are set when the table component renders the cell. 

    Wednesday, September 9, 2020 2:57 PM
  • User-343630552 posted

    Thanks for the quick replies.  I'm sure the cell components have row and column properties.  The problem is that none of the oninput, onkeydown, and onchange eventargs appear to provide any reference to the cell.  They only provide the new data.  Since the validation is dependent on which cell was changed, I have no way to tell which validation to use.  

    In case it helps, here is the part of the component markup for the cell and the event handler for oninput.

    Steve

    <td contenteditable="true" >
    <input @bind=@TableValue
                @oninput="BBDetail_OnInput" />
    </td>

    private void BBDetail_OnInput(ChangeEventArgs e)
            {
            var x0 = e.Value;
            }


    Wednesday, September 9, 2020 3:19 PM
  • User-474980206 posted

    as you are using the html components <table>, <tr> and <td> they do not have row and column properties. you will need to pass:

    <td contenteditable="true" >
       <input @bind=@TableValue
             @oninput="@((ChangeEventArgs e) => BBDetail_OnInput(e, 1, 2))" />
    </td>

    private void BBDetail_OnInput(ChangeEventArgs e, int row, int col) { var x0 = e.Value; }

    of course you might use variable for the row/col rather than hard code. if you wrapped the input with a custom component, then you could have props for the role and col.

    your other option is to use javascript interop and calc the row col. if you had a unique id for the input, javascript could walk the dom, and calc the row and column, using the .parent and .sibling properties to count the td in tr (col), and tr in trbody (row)

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, September 9, 2020 6:15 PM
  • User-343630552 posted

    Thanks, Bruce.  Great suggestions, as always.  I especially like the first one since the <td> elements are generated inside row and column @for loops already (so adding props will be easy) and I'd like to minimize my javascript for simplicity and to reuse a bunch of existing c# code.

    Wednesday, September 9, 2020 7:14 PM
  • User475983607 posted

    Array example.

    @page "/tablecellevent"
    <h3>@message</h3>
    
    <table class="table">
        <thead>
            <tr>
                <th>Col 1</th>
                <th>Col 2</th>
                <th>Col 3</th>
                <th>Col 4</th>
                <th>Col 5</th>
            </tr>
        </thead>
        <tbody>
    
            @for (int row = 0; row < Grid.GetLength(0); row++)
            {
            <tr>
                @for (int col = 0; col < Grid.GetLength(1); col++)
                {
                    var ov = Grid[row, col];
                    var r = row;
                    var c = col;
                    <td>
                        <input type="text" 
                               name="@("Cell" + ov.ToString())" 
                               @onchange="@(e => OnUpdateGrid(e, r, c, ov))" 
                               value="@ov" />
                    </td>
    
                }
            </tr>
            }
        </tbody>
    </table>
    
    @code {
    
        private int[,] Grid;
        private string message = "Table Cell Event Tester";
    
        protected override void OnInitialized()
        {
    
            Grid = GetGrid();
        }
    
        private void OnUpdateGrid(ChangeEventArgs e, int row, int col, int originalValue)
        {
            string newValue = e.Value.ToString();
            if (newValue != originalValue.ToString())
            {
                message = $"You changed cell({row}, {col}) from {originalValue} to {e.Value}";
            }
    
            int[,] grid = Grid;
            grid[row, col] = int.Parse(newValue);
    
        }
    
    
        private int[,] GetGrid()
        {
            int[,] cells = new int[5, 5];
            int cell = 0;
            for (int row = 0; row < cells.GetLength(0); row++)
            {
                for (int col = 0; col < cells.GetLength(1); col++)
                {
                    cells[row, col] = cell++;
                }
            }
            return cells;
        }
    
    }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, September 9, 2020 7:22 PM
  • User-343630552 posted

    Thanks for the complete solution.

    Wednesday, September 9, 2020 7:42 PM