[英]Blazor/razor onclick event with index parameter
I have the below code but the index parameter that is passed when I click the <tr>
element is always 9.我有下面的代码,但是当我单击<tr>
元素时传递的索引参数始终是 9。
That is becuase I have 9 rows in the table that is passed to the component as data.那是因为我在表中有 9 行作为数据传递给组件。 So looks like the index is always the value of variable 'i' which was last set... in this case the value of i after the last row in foreach loop is 9 so i am getting the index parameter as 9 on clicking all the rows in the table...所以看起来索引始终是最后设置的变量“i”的值......在这种情况下,foreach 循环中最后一行之后的 i 的值是 9,所以我在单击所有时将索引参数设置为 9表中的行...
What is the issue in my code which is not setting the i
value for each row onclick.我的代码中没有为每行 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);
}
}
Actually your problem is about lambda that captures local variable.实际上,您的问题是关于捕获局部变量的 lambda 。 See the following simulation with a console application for the sake of simplicity.为简单起见,请参阅以下使用控制台应用程序的模拟。
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);
}
It will output 3, 3, 3
thrice rather than 0, 1, 2
.它将 output 3, 3, 3
三次而不是0, 1, 2
。
Quoted from the official documentation about EventCallback :引用自有关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}.";
}
}
Do not use a loop variable directly in a lambda expression, such as
i
in the preceding for loop example.不要在 lambda 表达式中直接使用循环变量,例如前面 for 循环示例中的i
。 Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas.否则,所有 lambda 表达式都使用相同的变量,这会导致在所有 lambda 中使用相同的值。 Always capture the variable's value in a local variable and then use it.始终在局部变量中捕获变量的值,然后使用它。 In the preceding example, the loop variable i is assigned tobuttonNumber
.在前面的示例中,循环变量 i 被分配给buttonNumber
。
So you need to make a local copy for i
as follows.所以你需要为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>
}
This happens because the value of i
isn't rendered to the page (as it would have been in MVC/Razor Pages), it's just evaluated when you trigger the event.发生这种情况是因为i
的值没有呈现到页面(就像在 MVC/Razor 页面中一样),它只是在您触发事件时进行评估。 You won't trigger the event until the page has rendered, and so by that point the loop will have completed, so the value for i
will always be the value at the end of the loop.在页面呈现之前,您不会触发事件,因此到那时循环将完成,因此i
的值将始终是循环结束时的值。
There are a couple of ways to deal with this, either use a foreach
loop instead if that's suitable (which is what most of the Blazor documentation examples do), or declare a local variable inside the loop:有几种方法可以解决这个问题,或者在合适的情况下使用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
}
There's a good discussion of this in the Blazor Docs repo here , which explains the problem as follows: Blazor Docs repo 中对此进行了很好的讨论,该问题解释如下:
Problem is typically seen in event handlers and binding expressions.问题通常出现在事件处理程序和绑定表达式中。 We should explain that in
for
loop we have only one iteration variable and inforeach
we have a new variable for every iteration.我们应该解释一下,在for
循环中,我们只有一个迭代变量,而在foreach
中,每次迭代都有一个新变量。 We should explain that HTML content is rendered whenfor
/foreach
loop is executed, but event handlers are called later.我们应该解释一下,HTML 内容是在执行for
/foreach
循环时呈现的,但稍后会调用事件处理程序。 Here is an example code to demonstrate one wrong and two good solutions.这是一个示例代码来演示一个错误和两个好的解决方案。
This is all particularly confusing if you're coming to Blazor from an MVC/Razor Page background, where using for
is the normal behaviour.如果您从 MVC/Razor 页面背景进入 Blazor,这一切都特别令人困惑,其中使用for
是正常行为。 The difference is that in MVC the value of i
is actually written to the html on the page, and so would be different for each row of the table in your example.不同之处在于,在 MVC 中, i
的值实际上是写入页面上的 html 的,因此对于您的示例中的表格的每一行来说都是不同的。
As per the issue linked above and @Fat Man No Neck's answer, the root cause of this is down to differences in the behaviour of for
and foreach
loops with lambda expressions.根据上面链接的问题和@Fat Man No Neck 的回答,其根本原因在于for
和foreach
循环与 lambda 表达式的行为差异。 It's not a Blazor bug, it's just how C# works.这不是 Blazor 错误,而是 C# 的工作原理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.