HTML Parser Issues

Jul 25, 2012 at 9:32 AM
Edited Jul 25, 2012 at 9:59 AM

I've been having troubles building templates that work in the RazorEngine...

Something as simple as:

@for (int i = 0; i < Model.AmountOfCompanies; i++) {
<li><a href="tab_@i">@Model.Companies[i].UniversalName</a></li>
}

Causes the RazorEngine to choke when trying to compile.

{"End of file was reached before the \"a\" tag could be parsed.  Elements inside markup blocks must be complete. They must either be self-closing (\"<br />\") or have matching end tags (\"<p>Hello</p>\").  If you intended to display a \"<\" character, use the \"&lt;\" HTML entity."}

I've tried splitting up my templates into simpler parts, but more basic then the above example isn't possible.

Is there a way to kill the HTML parsing and just replace what I feed into it?

 

Even weirder:

If I change my template to do a String.Format() like:

 

@for (int i = 0; i < Model.AmountOfCompanies; i++) {
    Raw(String.Format("<li><a href=\"tab_{0}\">{1}</a></li>",i, Model.Companies[i].UniversalName));
}

 

, it will not throw a compile error, but it also doesn't produce output :( I use this in a larger template (which was split because I was trying to find a way to get this working:

 

<div id="Tabs" class="tabs">
@if (Model.AmountOfCompanies > 0) {
    <ul>    
    @Include("TabHeaders", Model)
    </ul>
}
</div>

 

It outputs:

 

<div id="Tabs" class="tabs">
    <ul>    
    </ul>
</div>

 

If I change my first template to get some more information like: 

 

@for (int i = 0; i < Model.AmountOfCompanies; i++) {
    Raw(String.Format("<li><a href=\"tab_{0}\">{1}</a></li>",i, Model.Companies[i].UniversalName));
}
<p>First: @Model.Companies[0].UniversalName; Available: @Model.AmountOfCompanies</p>
<p>Format Test: @Raw(String.Format("<li><a href=\"tab_{0}\">{1}</a></li>",0, Model.Companies[0].UniversalName))</p>

 

The output becomes:

 

<div id="Tabs" class="tabs">
    <ul>    
    <p>First: carl-zeiss-microscopy-gmbh; Available: 2</p>
    </ul>
</div>

 

Then, I tested to see if the Raw or String.Format didn't work; but those also work:

 

<div id="Tabs" class="tabs">
    <ul>    
    <p>First: carl-zeiss-microscopy-gmbh; Available: 2</p>
<p>Format Test: <li><a href="tab_0">carl-zeiss-microscopy-gmbh</a></li></p>
    </ul>
</div>

So, WTF? Why is RazorEngine f-ing up in loops, and how to I fix this? If I have to hack a solution myself, the usage of the RazorEngine itself becomes more of a hassle then a relief...

 

Next, I figured I'd build the HTML String, and return it later. This showed me that 'writing' html inside code doesn't work; doing it inline inside HTML does work.

Template PoC:

 

<div id="Tabs" class="tabs">
@if (Model.AmountOfCompanies > 0) {
    <ul>    
    @Include("TabHeaders", Model)
    </ul>
}
</div>

<!-- Returning data inside a code block DOES NOT WORK -->
@{
	String Items = "";
	for (int i = 0; i < Model.AmountOfCompanies; i++) {
		// RazorEngine cannot output HTML inside loops, so collect & output later.
		Items += String.Format("\r\n<li><a href=\"tab_{0}\">{1}</a></li>",i, Model.Companies[i].UniversalName);
	}
	// Does not work!
	Raw(Items);
}
<!-- Works! -->@Raw(Items)

 

Output:

 

<div id="Tabs" class="tabs">
    <ul>    
    <!-- Returning data inside a code block DOES NOT WORK -->
<!-- Works! -->
<li><a href="tab_0">carl-zeiss-microscopy-gmbh</a></li>
<li><a href="tab_1">carl-zeiss-vision</a></li>
    </ul>
</div>

As you can tell; there isn't much left of the template... Any chance this will get fixed? If not, please point me to a similar engine that does work...

Coordinator
Aug 8, 2012 at 4:36 PM

The issue you seem to be experiencing with your initial loop code is that the @ transition character needs to be followed by a explicit expression, i.e:

 

<li><a href="tab_@i">@Model.Companies[i].UniversalName</a></li>

Needs to be:

<li><a href="tab_@(i)">@Model.Companies[i].UniversalName</a></li>

In terms of using @Raw in a code block, when you use @{ ... }, the code you place here become part of the method body, which means simply calling Raw(...) inside it does nothing with Raw()'s return value.

When you use @Raw in your template, the @Raw(...) method call return value is written to the output text writer.

 

Aug 9, 2012 at 8:32 AM

Thanks for your reply. While I appreciate the feedback, by now, I just switched to handlebars and compile the templates on the client side with JSON data. While I would have loved to use Razor, to be able to make the move to MVC at a later point less painfull for the huge application we have right now, the fact that the parser can't report on these issues and instead throw weird other errors, I'll just continue on the route I am right now.

I have no idea how complex it would be to improve the parser to report on these things, but I'd love to write it myself if only I had the time :-/