Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

if block inside foreach block doesn't parse ending brackets correctly #679

Closed
esargent opened this issue Jan 27, 2016 · 7 comments
Closed
Assignees
Milestone

Comments

@esargent
Copy link

I think this is different from #51 but adding the link in case there is a relation.

Using RC2-16770

My view iterates through a collection of items (Practitioners) and displays some things. That works great UNTIL I add an if block inside the foreach. Then razor finds the closing bracket for the foreach, but associates it to the parent bracket enclosing the overall razor code block. This happens both in VS2015 code highlighting and when the code is executed. Also note, I can add an if statement with a single line - it is when I add the brackets to designate a code block that the issue occurs.

Works:

 @model DistributorPortalViewModel
 <div class="">
 <h1>Simple Customer List</h1>
 <div>Click a name to expand for details.</div>
 <hr />
@{
    string target, targetId, currentClinic = "";
    foreach (Practitioner p in Model.Practitioners)
    {
        targetId = "collapseArea" + @p.Id;
        target = "#" + targetId;
        <div class="m5">
            <a data-toggle="collapse" href=@target>@Html.DisplayFor(modelItem => p.LastName)</a>
            <div id=@targetId class="collapse">
                @Html.DisplayFor(modelItem => p.Name) at @Html.DisplayFor(modelItem => p.PrimaryClinic.Name)<br />
                Is Listed in Directory? @Html.DisplayTextFor(modelItem => p.IsListed)
            </div>
        </div>
}
}
<hr />

Fails:

@model DistributorPortalViewModel
<div class="">
<h1>Simple Customer List</h1>
<div>Click a name to expand for details.</div>
<hr />
@{
    string target, targetId, currentClinic = "";
    foreach (Practitioner p in Model.Practitioners)
    {
        targetId = "collapseArea" + @p.Id;
        target = "#" + targetId;
        if (currentClinic != p.PrimaryClinic.Name)
        {
            currentClinic = p.PrimaryClinic.Name;
            <h2>@p.PrimaryClinic.Name</h2>
            }
        <div class="m5">
            <a data-toggle="collapse" href=@target>@Html.DisplayFor(modelItem => p.LastName)</a>
            <div id=@targetId class="collapse">
                @Html.DisplayFor(modelItem => p.Name) at @Html.DisplayFor(modelItem => p.PrimaryClinic.Name)<br />
                Is Listed in Directory? @Html.DisplayTextFor(modelItem => p.IsListed)
            </div>
        </div>
}
}
<hr />

the only difference is the insertion of the if block inside the for each:

            if (currentClinic != p.PrimaryClinic.Name)
        {
            currentClinic = p.PrimaryClinic.Name;
            <h2>@p.PrimaryClinic.Name</h2>
            }

Tried to distill this to the simplest possible:

@model DistributorPortalViewModel
@{
    string targetId, c = "";
    foreach (var p in Model.Practitioners)
    {
        targetId = @p.Name;
        if (c != p.Name)
        {
            <div>something</div>
        }
    }
}

It seems you have to have at least one line of code with razor sytax in it prior to the if block or it will parse correctly.

@NTaylorMullen
Copy link
Member

Does this work at runtime for you or is it purely a tooling issue?

@esargent
Copy link
Author

Fails in both places - didn't really notice it in tooling until it failed while trying to run.

@esargent
Copy link
Author

additional info:
I rebooted after reporting, but that didn't make a difference. Same problem.

I thought I would outsmart it by putting two single-line if statements with the code to execute on the same line and no {block}. This time it is even stranger:

    @model DistributorPortalViewModel
    <div class="">
    <h1>Simple Customer List</h1>
    <div>Click a name to expand for details.</div>
    <hr />
    @{
       string target, targetId, currentClinic = "";
       foreach (Practitioner p in Model.Practitioners)
       {
           targetId = "collapseArea" + @p.Id;
           target = "#" + targetId;
           if (currentClinic != p.PrimaryClinic.Name) currentClinic = p.PrimaryClinic.Name; 
           if (currentClinic != p.PrimaryClinic.Name)  <h2>@p.PrimaryClinic.Name</h2>    
        <div class="m5">
            <a data-toggle="collapse" href=@target>@Html.DisplayFor(modelItem => p.LastName)</a>
            <div id=@targetId class="collapse">
                @Html.DisplayFor(modelItem => p.Name) at @Html.DisplayFor(modelItem => p.PrimaryClinic.Name)<br />
                Is Listed in Directory? @Html.DisplayTextFor(modelItem => p.IsListed)
            </div>
        </div>
            if (currentClinic != p.PrimaryClinic.Name) { currentClinic = p.PrimaryClinic.Name; }

        }
    }
<hr />

This code executes, but not as expected, and the tooling parser is strange too.

a> at the bottom notice that there is an if {block} but it doesn't break things. I can't figure out the difference, but it works here while a block towards the top, as in the previous example, breaks (in runtime and tooling)

b> In this example, if I do NOT put brackets around block for the last if, I get a really nice error warning in Visual Studio that single-statement control flow statements are not allowed in cshtml and you have to use brackets. That makes sense, BUT, I don't get the same warning with the two if statements above. So what is the difference?

c> When this page executes, in my sample dataset there is only one clinic, so when this runs you'd expect the first if statement to evaluate true and update currentClinic variable. It does that. And in subsequent iterations you'd expect to evaluate false because they are now equal. It does that. BUT the second if condition, which is exactly the same, ALWAYS evaluates true and renders the <h2> block in each iteration, even though the values are now the same.

the debugger sees both sides of the conditional as strings and you can clearly see they are the same, but the != evals to true for the second if while evaluating false for the first one!

Maybe issue#c is due to the lack of brackets and isn't supported, but hopefully all this info helps you track it down.

@Eilon
Copy link
Member

Eilon commented Jan 29, 2016

@NTaylorMullen can you see if there's a safe fix for this?

@NTaylorMullen
Copy link
Member

So I played around with this and it turns out your @p.Id is the culprit. If you remove the @ it all works. My semi-minimal repro is:

@{
    foreach (int x in new[] { 1, 2, 3 })
    {
        var val = @x;
        if (val != 3) { }
    }
}

I'll work on getting a fix for this

NTaylorMullen added a commit that referenced this issue Mar 3, 2016
- We used to accept until markup or an ending brace which didn't allow the parser to balance nested braces.

#679
@NTaylorMullen
Copy link
Member

e6d4d6c

@gianfrancocalautti
Copy link

I resolved by replacing the if statement with a switch statement in NET framework 4.6.1

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants