28

I have lots of complex HTML reports in my current project where there we perform lots of conditional rendering of TRs and TDs with rowspans and colspans.

It could sometimes look like this (this is extremely simplified):

<tr>
@foreach (var ourItem in ourList) {
   if (ourItem != ourList.First()) {
      <tr>                
   }
   <td></td>
   </tr>
}

However, Razor claims: "The foreach loop is missing a closing "}" character". (within Visual Studio)

I've tried to wrap the <tr> in <text></text> which makes the closing } problem go away only to find this when run: "Encountered end tag "tr" with no matching start tag. Are your start/end tags properly balanced".

How would I do this kind of conditional rendering while convincing Razor not to bother about the HTML at all, cause the HTML is balanced when all the looping is done. Or at least that was the case when the ASP.NET View Engine was used.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
Mikael Östberg
  • 16,982
  • 6
  • 61
  • 79
  • From the looks of your code, shouldn't the whole tr and /tr piece be within the same if condition. I haven't used razor but what you have shown seems confusing. If the condition is not met then the /tr tag has the first tr as the starting tag and if condition is met, then its for the second tr tag and the first one does not have a corresponding /tr tag. Just something I observed. – shashi Jan 24 '11 at 12:28
  • 1
    The above code may be stupid, but it is valid. When rendered into HTML anyway. The point I trying to prove here is that even if the rendered result would be valid HTML, Razor comes in and breaks it because it cannot accept the weird semantics that it thinks this will produce. – Mikael Östberg Jan 24 '11 at 13:08
  • @sassyboy If ourList is non-empty then there should always be matching trs. The open tr will either be the one outside the loop for the first item in the list or the one inside the condition for subsequent items. – Rup Jan 24 '11 at 13:10
  • To keep things at a minimal, this is the code that we have to focus on. Let's pretend that the list is non-empty. Given this I want to make Razor to render it. – Mikael Östberg Jan 24 '11 at 13:20

4 Answers4

36

Visual Studio Intellisense and syntax highlighting is not one of the best but in this case it warns you that if the condition is not satisfied, you might get invalid markup and you shouldn't blame it for this.

The important thing is that your project runs fine but you might consider externalizing this logic into HTML helpers because if what you are saying is true that this is a simplified version of what you have in the views I don't even want to imagine how your actual code looks.

IMHO having so much conditional logic in a view is an abuse. You definitely should consider using HTML helpers or controls such as MVCContrib Grid.


UPDATE:

You may try the following hack:

<tr>
@foreach (var ourItem in ourList) {
   if (ourItem != ourList.First()) {
      @:<tr>                
   }
   @:<td></td>
   @:</tr>
}
niico
  • 11,206
  • 23
  • 78
  • 161
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Oh, I get the exact same error when I am running it in Cassini. Basically the problem is that Razor takes responsibility for the HMTL being rendered. I would like to shut that off really. – Mikael Östberg Jan 24 '11 at 13:17
  • i am agree with you. in SP1 their is no tag collapsing feature for this may be we use CTRL M + T. sometime they show you error even code is fine work fine and at-least no problem but VS says syntax missing. –  Jan 24 '11 at 18:26
  • 6
    Up voted for the @: option in your update, which I don't consider a hack. – Michiel van Oosterhout Nov 07 '11 at 16:29
  • @DarinDimitrov: @: doesnt work for me. it throws parser error. – Vini Oct 08 '15 at 12:07
  • What can be the possible reasons that it isnt working? i have a for loop and an if loop before the closing tag. – Vini Oct 08 '15 at 12:08
  • I think visual studio is one of the best? What is better? – niico Oct 28 '16 at 14:14
6

Razor depends on matching tags to determine the automatic transitions between code and markup. You cannot "disable" this feature of Razor (at least not without rewriting large parts of the Razor parser).

You can work around it by using Darin's suggestion, though I don't understand (at least not from your simplified example) why your view needs to be so convoluted. Why not write the following code instead:

@foreach (var ourItem in ourList) {
   <tr>
   <td>...</td>
   </tr>
}

While tags might balance out in the generated markup, the source that you provided makes it very difficult to reason about its correctness.

marcind
  • 52,944
  • 13
  • 125
  • 111
4

When trying to use rowspan and trying to get a structure like

<table>
   <tr>
       <td rowspan=2>1:st col</td>
       <td>2:nd col</td>
   </tr>
   <tr>
       <td>2:nd col</td>
   </tr>
</table>

You could try:

@{ 
    var ourList = new List<string> { "1", "2", "3" };
}

<table border=1>
@foreach(var ourItem in ourList){
    <tr>
    @if (ourItem == ourList.First())
    {
        <td rowspan="@ourList.Count()">@ourItem</td>
    }
    <td>@ourItem</td>
    </tr>
}
</table>
Wiren
  • 477
  • 3
  • 9
4

I had a similar problem - my solution Html.Raw("");

    if (isTrue)
    {
        <text><div></text> }
   ...

    if(isTrue) {
        @Html.Raw("</div>"); // <-- closing tag!
        }
Robert Muehsig
  • 5,206
  • 2
  • 29
  • 33