Skip to content

foreach loop in context of lambda expressions #10768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
guardrex opened this issue Feb 2, 2019 — with docs.microsoft.com · 2 comments
Closed

foreach loop in context of lambda expressions #10768

guardrex opened this issue Feb 2, 2019 — with docs.microsoft.com · 2 comments
Assignees
Milestone

Comments

Copy link
Collaborator

guardrex commented Feb 2, 2019

From @Andrzej-W on aspnet/Blazor.Docs#371 ...

In the current doc we have plenty of examples with foreach loops, but people often use for loops and have problems. Although it is a general C# topic and not Blazor specific, we should add some info and explain the difference. Otherwise we will have more "bug reports" like these: https://github.com/aspnet/Blazor/issues/665, https://github.com/aspnet/Blazor/issues/764, https://github.com/aspnet/Blazor/issues/1402, dotnet/aspnetcore#6591.

Notice that I selected only a few examples and there are a lot of similar issues.

Problem is typically seen in event handlers and binding expressions. We should explain that in for loop we have only one iteration variable and in foreach we have a new variable for every iteration. We should explain that HTML content is rendered when for / foreach loop is executed, but event handlers are called later. Here is an example code to demonstrate one wrong and two good solutions.

<div>Item clicked: @Item List: @List</div>
<br />
<div>List A</div>
@for (var i = 0; i < 3; i++)
{
    // This is wrong. Each <div> element, when clicked, will call ItemClicked
    // with the current value of variable i which is 3 at the end of the loop.
    <div onclick=@(_ => ItemClicked(i, 'A'))>Item @i</div>
}
<br />
<div>List B</div>
@for (var i = 0; i < 3; i++)
{
    int localVar = i;
    <div onclick=@(_ => ItemClicked(localVar, 'B'))>Item @localVar</div>
}
<br />
<div>List C</div>
@foreach (var i in intTable)
{
    <div onclick=@(_ => ItemClicked(i, 'C'))>Item @i</div>
}
@functions{
    int Item { get; set; } = -1;
    char List { get; set; } = '?';
    int[] intTable = { 0, 1, 2 };

    void ItemClicked(int value, char list)
    {
        Item = value;
        List = list;
    }
}

Unfortunately English is not my native language and probably I'm not the proper person to write this.

I'm also not sure where to add this information, maybe here: https://blazor.net/docs/components/index.html#event-handling

From @Andrzej-W ...

Here is yet another example with binding expressions.

Binding expression in the first for loop doesn't work correctly. Loop has one less iteration to remove the risk of exception (array index out of range). Page is rendered correctly because it happens when for loop is executed. Unfortunately in binding expression we use variable i which will have value equal 3 at the and of the loop. Try to edit any customer in the first list and press TAB key or click outside of the field. Notice that list B and C is refreshed and you edited fourth (last) customer which is not even displayed in the first list.

<div>List A</div>
@for (var i = 0; i < customers.Length - 1; i++)
{
    // This binding expression doesn't work correctly.
    <div><input bind="@customers[i].Name" /></div>
}
<br />
<div>List B</div>
@for (var i = 0; i < customers.Length; i++)
{
    int localVar = i;
    <div><input bind="@customers[localVar].Name" /></div>
}
<br />
<div>List C</div>
@foreach (var cust in customers)
{
    <div><input bind="@cust.Name" /></div>
}
@functions{
    public class Customer
    {
        public string Name { get; set; }
    };

    Customer[] customers = new Customer[]
    {
        new Customer { Name = "Cust A" },
        new Customer { Name = "Cust B" },
        new Customer { Name = "Cust C" },
        new Customer { Name = "Last customer" }
    };
}

Probably the best place to add this example is here https://blazor.net/docs/components/index.html#data-binding a little above event handling.

Eventually here https://blazor.net/docs/tutorials/build-your-first-blazor-app.html#build-a-todo-list in step number 17 we can add warning that if someone wants to use for loop he/she should read section about data binding in components.

From @guardrex ...

Thanks for opening this issue @Andrzej-W. Your points and explanation are excellent.

Before we discuss what to do in the Blazor docs, let's call in for advice ...

BillWagner 👋 TL;DR Devs are getting tripped up using for with lambdas and binding in Blazor code; consequently, they're opening issues to ask about potentially broken Razor/Blazor API when the real problem is how they're using C#. @Andrzej-W links to some of these issues in his opening post here. ☝️

I'd like to link to C# Guide content (or other authoritative content) and provide as little original content in the Blazor docs as possible on these points. I took a look at ...

I don't see the conceptual difference that we'd like to highlight for this scenario laid out plainly ... not in a form that we can link, such as a specific section that goes directly to these points.

What's your advice, and are you aware of content that we can link in a MS doc set (or perhaps another trusted doc set)?

From @Andrzej-W ...

@guardrex , BillWagner I have found this "trusted doc":

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#variable-scope-in-lambda-expressions

Unfortunately this is probably not enough for some people to understand the problem:

Lambdas can refer to outer variables (see Anonymous Methods) that are in scope in the method that defines the lambda function, or in scope in the type that contains the lambda expression. Variables that are captured in this manner are stored for use in the lambda expression even if the variables would otherwise go out of scope and be garbage collected.

You can link to this doc but good examples as demonstrated above are necessary.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

@dotnet-bot dotnet-bot added the Source - Docs.ms Docs Customer feedback via GitHub Issue label Feb 2, 2019
@guardrex guardrex added this to the Backlog milestone Feb 2, 2019
@guardrex guardrex changed the title foreach loop in context of lambda expressions in Razor Components topics foreach loop in context of lambda expressions Feb 2, 2019
@danroth27 danroth27 added Pri1 and removed DT labels Feb 5, 2019
@qiangboy
Copy link

good

@ajaybhargavb
Copy link
Contributor

#11152

Rick-Anderson pushed a commit to dotnet/AspNetDocs that referenced this issue Mar 17, 2019
* Add a policy schemes doc (#11150)

* Create policyschemes.md

* Update policyschemes.md

* Update policyschemes.md

* Update policyschemes.md (#11154)

[Internal review URL](https://review.docs.microsoft.com/en-us/aspnet/core/security/authentication/policyschemes?view=aspnetcore-1.0&branch=pr-en-us-11154)

* Added note about deprecated features (#11098)

* Added note about deprecated these features

Added a note that the web performance and load testing tools in Visual Studio is deprecated and will be removed after VS 2019 as was announced here: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-preview#test-tools

In the same note, a line was added about the load testing service in Azure DevOps closing in 2020 from the announcement here: https://devblogs.microsoft.com/devops/cloud-based-load-testing-service-eol/

* Update load-tests.md

* Update load-tests.md

* Fix glob pattern in .gitignore (#11165)

* Add async iterator sample to the SignalR streaming doc (#11140)

* Clarify default proxy addresses (#11167)

Fixes #10651

Approved on the issue by engineering.

* 2.2 example for AzureWebAppDiagnostics (#11169)

Fixes #11029 
[Internal review URL](https://review.docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/index?view=aspnetcore-2.2&branch=pr-en-us-11169#logging-in-azure)

* Move youtube link (#11176)

* youtube link (#11177)

* youtube link (#11178)

* youtube link (#11179)

* youtube link (#11181)

* youtube link (#11183)

* youtube link (#11185)

* youtube link (#11186)

* youtube link (#11180)

* youtube link

* Update sql.md

* youtube link (#11188)

* Patch bookmarks (#11190)

Patch a few broken bookmarks.

* RP route and app conventions update (#11194)

Fixes #6801 

* Add example code for `IPageHandlerModelConvention` to the sample app (but it's not wired up to do anything really ... just shows how to implement it generally).
* Take sample app to 2.2.
* Drop the *Replace the default page app model provider* and sample code for it per instructions from engineering (see the issue).
* Take topic to 2.1+ (shed 2.0 content).
* XREF links (I really suspect that a VSC Extension could do 90% of the conversion work ... I should consider creating it.)

* Document bind-property-event syntax and add a note about lambda expressions (#11152)

Fixes dotnet/AspNetCore.Docs#10769 and dotnet/AspNetCore.Docs#10768

* updated referenced JS files (#11193)

`blazor.server|webassembly.js` are now `components.server|webassembly.js` as of ASP.NET Core 3.0 preview 2

* Updated namespace usage (#11191)

* Update BlazorLayoutComponent > LayoutComponentBase (#11189)

* Update BlazorLayoutComponent LayoutComponentBase (#11187)

* Update BlazorLayoutComponent > LayoutComponentBase (#11184)

* Update startup.md (#11200)

* Add Form Action Tag Helper docs (#11192)

- Utilized the `AnchorTagHelper` docs to consistenly describe the `asp-` attributes used to control how links get generated.
- Updated the built in TagHelper list doc to include the FormAction TagHelper.

#10593 
[Review URL](https://review.docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-2.2&branch=pr-en-us-11192#the-form-action-tag-helper)

* Update cicd.md (#11113)

Closes #11074

* Add more ANCM startup failures (#11155)

* Update logging to 2.2 (#11207)

Also convert inline code snippet to reference.

Fixes #11206 

[Internal review URL](https://review.docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/index?view=aspnetcore-2.2&branch=pr-en-us-11207#azure-app-service-provider)
<!--
# Instructions

When creating a new PR, please reference the issue number if there is one:

Fixes #Issue_Number

The "Fixes #nnn" syntax in the PR description allows GitHub to automatically close the issue when this PR is merged.

NOTE: This is a comment; please type your descriptions above or below it.
-->

* Update Redis section of dist caching (#11160)

* Add snippet about how to configure OIDC options (#11182)

* Add snippet about how to configure OIDC options

* Update azure-ad-b2c.md

* Update aspnetcore/security/authentication/azure-ad-b2c.md

Co-Authored-By: HaoK <HaoK@users.noreply.github.com>

* Update aspnetcore/security/authentication/azure-ad-b2c.md

Co-Authored-By: HaoK <HaoK@users.noreply.github.com>

* Update azure-ad-b2c.md

* Update azure-ad-b2c.md

* Refactor code snippet

* More code snippet refactoring

* updating and removing aspnet/docs (aspnetcore)

* Update docfx.json (#3)
@guardrex guardrex removed the Source - Docs.ms Docs Customer feedback via GitHub Issue label May 23, 2019
@Rick-Anderson Rick-Anderson moved this to Done in Blazor.Docs Oct 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

5 participants