【问题标题】:Blazor/razor onclick event with index parameter带有索引参数的 Blazor/razor onclick 事件
【发布时间】:2020-10-30 23:48:12
【问题描述】:

我有以下代码,但是当我单击<tr> 元素时传递的索引参数始终为 9。

那是因为我在表中有 9 行作为数据传递给组件。 所以看起来索引始终是最后设置的变量“i”的值......在这种情况下,foreach 循环中最后一行之后的 i 的值是 9,所以我在单击所有时将索引参数设为 9表中的行...

我的代码中没有为每行 onclick 设置 i 值的问题是什么。

    <table border="1">

        @for(int i=0;i< ListData.DataView.Table.Rows.Count; i++)
        {
            <tr @onclick="(() => RowSelect(i))">
                @foreach (ModelColumn col in ListData.ListColumns)
                {
                    <td>@ListData.DataView.Table.Rows[i][col.Name]</td>
                }
            </tr>
        }

    </table>
@code {
 
    private async Task  RowSelect(int rowIndex)
    {
        await ListRowSelected.InvokeAsync(rowIndex);
    }
}

【问题讨论】:

  • 在将计数器i 传递给 lambda 之前,您需要对其进行缓冲。

标签: c# razor-pages blazor-server-side


【解决方案1】:

一般

实际上,您的问题是关于捕获局部变量的 lambda。为简单起见,请参阅以下使用控制台应用程序的模拟。

class Program
{
    static void Main(string[] args)
    {
        Action[] acts = new Action[3];

        for (int i = 0; i < 3; i++)
            acts[i] = (() => Job(i));

        foreach (var act in acts) act?.Invoke();
    }

    static void Job(int i) => Console.WriteLine(i);
}

它将输出3, 3, 3三次而不是0, 1, 2

Blazor

引用自官方文档关于EventCallback:

<h2>@message</h2>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <button class="btn btn-primary"
            @onclick="@(e => UpdateHeading(e, buttonNumber))">
        Button #@i
    </button>
}

@code {
    private string message = "Select a button to learn its position.";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber)
    {
        message = $"You selected Button #{buttonNumber} at " +
            $"mouse position: {e.ClientX} X {e.ClientY}.";
    }
}

不要在 lambda 表达式中直接使用循环变量,例如前面 for 循环示例中的 i。否则,所有 lambda 表达式都使用相同的变量,这将导致在所有 lambda 中使用相同的值。始终在局部变量中捕获变量的值,然后使用它。在前面的示例中,循环变量 i 被分配给 buttonNumber

因此您需要为i 制作一个本地副本,如下所示。

@for(int i=0;i< ListData.DataView.Table.Rows.Count; i++)
{
   int buffer=i;
    <tr @onclick="(() => RowSelect(buffer))">
        @foreach (ModelColumn col in ListData.ListColumns)
        {
            <td>@ListData.DataView.Table.Rows[i][col.Name]</td>
        }
    </tr>
}

【讨论】:

  • 非常感谢!!!它有效...我需要定义一个 int 然后每次分配的确切原因是什么?
  • 谢谢它确实有效......明白了!我知道我需要在传递给 lambda 时这样做。
  • @Codegeek:详情请阅读answer (and the given links)
【解决方案2】:

发生这种情况是因为i 的值没有呈现到页面(就像在 MVC/Razor 页面中一样),它只是在您触发事件时进行评估。在页面呈现之前您不会触发事件,因此到那时循环将完成,因此i 的值将始终是循环结束时的值。

有几种方法可以解决这个问题,或者在合适的情况下使用 foreach 循环(这是大多数 Blazor 文档示例所做的),或者在循环内声明一个局部变量:

@for(int i=0;i< ListData.DataView.Table.Rows.Count; i++)
{
   int local_i=i;

    // Now use local_i instead of i in the code inside the for loop
}

有一个good discussion of this in the Blazor Docs repo here,对问题的解释如下:

问题通常出现在事件处理程序和绑定表达式中。 我们应该解释一下,在for 循环中,我们只有一个迭代变量 在foreach 中,每次迭代都有一个新变量。我们应该 说明当for/foreach循环是HTML内容 执行,但稍后调用事件处理程序。这是一个示例代码 展示一个错误和两个好的解决方案。

如果您从 MVC/Razor 页面背景进入 Blazor,这一切都会特别令人困惑,其中使用 for 是正常行为。不同之处在于,在 MVC 中,i 的值实际上是写入页面上的 html,因此对于您的示例中表格的每一行来说都是不同的。

根据上面链接的问题和@Fat Man No Neck 的回答,其根本原因在于forforeach 循环与lambda 表达式的行为差异。这不是 Blazor 错误,而是 C# 的工作原理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-20
    • 2020-10-08
    • 2022-10-03
    • 1970-01-01
    • 2020-01-31
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    相关资源
    最近更新 更多