Expressions

Simple csharp expressions

Any csharp syntax can appear as ${expression} or <%=expression%>

The expression text itself is reproduced verbatim as a TextWriter.Write(expression); line. So anything that evaluates to a string, simple type, or object with ToString can be used in an expression.

<div>
  <p>The time is ${DateTime.Now}.</p>
  <p>The time is <%=DateTime.Now%>.</p>
</div>

Nulls in expressions

By default when a NullReferenceException is thrown from the ${expressions} or <%=expression%> it will be caught and the original expression will be output. This is similar to the behavior you will find in NVelocity and is intended to assist troubleshooting simple data problems.

The syntax $!{expression} can also be used if you want to ensure any null values and NullReferenceException that result from the expression will produce no output at all.

For example, with the following template:

<div class="shipto">
  <p>$!{currentInvoice.ShipTo.FullName}</p>
  <p>$!{currentInvoice.ShipTo.Address.City}, $!{currentInvoice.ShipTo.Address.State} $!{currentInvoice.ShipTo.Address.Zip}</p>
</div>

If there is a null currentInvoice or a null ShipTo, the output would be:

<div class="shipto">
  <p></p>
  <p>,  </p>
</div>

It's not ideal by a long shot, but slightly better than a yellow screen of death if you don't manage to get a hundred percent correct null testing in your views.

Using Mvc Helpers

There are instances of HtmlHelper, UrlHelper, and AjaxHelper available as Html, Url, and Ajax properties respectively. Any of their methods which return string-friendly information can be used as an output expressions.

<!-- Link to the Sort action on the current controller -->
<p>${Html.ActionLink("Click me", "Sort")}</p>
<!-- Put out data in a way that's safe -->
<p>${Html.Encode(stuff)}</p>

Conditional elements if, test, and else

The elements <if> and <else> can be used to produce content conditionally. The if element must have a condition attribute which is used literally as the boolean csharp expression.

<var x='5'/>
 
<if condition='x == 5'>
  <p class='resultmessage'>Some value is five</p>
</if>

An <if> element may be followed by any number of optional <else> elements that have if attributes and finally a single, optional <else> element. Only whitespace may be between the if/else elements.

<viewdata user='UserInfo'/>
 
<if condition='!user.IsLoggedIn()'>
  <p>Here's a login form</p>
</if>
<else if='user.HasRole(RoleType.Administrator)'>
  <p>Hello - you're an admin</p>
</else>
<else if='user.HasRole(RoleType.Registered)'>
  <p>Hello - you're a registered user</p>
</else>
<else>
  <p>I have no idea what type of person you are</p>
</else>

If you prefer, instread of <if condition=""> you can use the <test if=""> syntax. The functionality is the same. Another variation on the syntax is the ability to use empty <else if=""/> and <else/> elements inside of the or . The following identical to the example above.

<viewdata user='UserInfo'/>
 
<test if='!user.IsLoggedIn()'>
  <p>Here's a login form</p>
  <else if='user.HasRole(RoleType.Administrator)'/>
  <p>Hello - you're an admin</p>
  <else if='user.HasRole(RoleType.Registered)'/>
  <p>Hello - you're a registered user</p>
  <else/>
  <p>I have no idea what type of person you are</p>
</test>

Conditional attributes if and elseif

The if and elseif attributes can be used on any other element. This has the same effect as having a wrapping if or else element.

<var x='5'/>
 
<p if='x==5' class='resultmessage'>Some value is five</p>

The same rules about order and whitespace apply. An element with an elseif="" attribute, or an else element, may only follow another conditional element.

<use namespace='SampleApp.Models'/>
<viewdata user='UserInfo'/>
 
<p if='!user.IsLoggedIn()'>Here's a login form</p>
<p elseif='user.HasRole(RoleType.Administrator)'>Hello - you're an admin</p>
<p elseif='user.HasRole(RoleType.Registered)'>Hello - you're a registered user</p>
<else>
  <p>I have no idea what type of person you are</p>
</else>

Conditional attribute once

A fairly typical situation can come up where a partial will need to add a stylesheet reference or some jquery code. On the one hand you don't want to have those resources pulled in multiple times if the partial is used more than once, and on the other hand you don't want to simply include that resource just once in the site layout for all pages whether they need it or not. One way to implement the middle ground is to declare a boolean flag and set it once when you add a script to the header.

To make this code smaller the attribute once="flagname" was added. The contents of the element will be rendered only the first time any given "flagname" values is used when rendering a page. Any conventions can be used about what values are used as flags, the following is just an example. You may also use ${expr} or <%=expr%> syntax in the value of the once attribute.

<content name="head">
  <!--  add jquery if it hasn't been yet, and add countdown plugin -->
  <script once="jquery" type="text/javascript" src="~/content/js/jquery-1.2.6.js"/>
  <script once="jquery-countdown" type="text/javascript" src="~/content/js/jquery.countdown.js"/>
</content>

The implementation if this attribute results in the bool Once(string flagname) method being called which returns true the first time a distinct value is used for the argument. This method may also be called from code.

<macro name="Peculiar" issue="string">
#if (Once("log-peculiar")) 
#  Logger.Warn("Something's not right on this page. First issue: " + issue);
</macro>

Conditional attribute output

The syntax ?{boolean} can be used only inside the text of an attribute. Text and code before the condition (up to and including the previous whitespace) will be controlled by the result of the boolean expression. In the generated code the boolean will appear directly inside the parenthesis of an if statement, so anything that evaluates to a bool is okay.

<ul>
  <li each="var product in Products" class="first?{productIsFirst} last?{productIsLast}">
    ${H(product.Name)}
  </li>
</ul>

will output

<ul>
  <li class="first">Alpha</li>
  <li>Beta</li>
  <li>Gamma</li>
  <li class=" last">Delta</li>
</ul>
or
<ul>
  <li class="first last">just one product</li>
</ul>

Plus if all of the text of an attribute is killed, or if the only thing in the attribute was the test, then the entire attribute will disappear from the output. That's for checkboxes and such.

<input type="checkbox" name="chkhello" checked="?{isHelloChecked}"></input>

will output

<input type="checkbox" name="chkhello" checked="checked"></input> <!-- true -->
or
<input type="checkbox" name="chkhello"></input> <!-- false -->

Looping and iteration

A foreach loop is produced with the element for and the attribute each. The each attribute is used verbatim so must have the type, variable name, "in", and collection.

<viewdata Posts="IList[[MyApp.Models.Post]"/>
<for each="var post in Posts">
  <p>${post.Title}</p>
</for>

Any for loop comes with several optional local variables. Their name is always the same as the looping variable, plus the suffixes "Index", "Count", "IsFirst", and "IsLast". The variables are scoped to the for loop itself, and only the ones which appear to be used in the contained code will be created.

<viewdata Posts="IList[[MyApp.Models.Post]"/>
<var styles="new[] {'even','odd'}"/>
<for each="var post in Posts">
  <p class="${styles[postIndex%2]}">${postIndex}. ${post.Title}</p>
</for>

Something to be aware with the use of the "Count" and "IsLast" variables - they both need to know how many items are in the collection before the iteration begins. For collections that resolve to IEnumerable<T> the Linq extension method .Count() is used, and for plain old IEnumerable Spark will run iterate the collection incrementing an integer to get the count. Not a performance problem of course, just worth mentioning.

<for each="var lineItem in CurrentInvoice.LineItems">
  <tr><td>Item ${lineItemIndex} of ${lineItemCount}</td><td>${lineItem.Etc}</td></tr>
  <tr if="${lineItemIsLast}"><td>Grand total</td><td>etc</td></tr>
</for>

The for element can also have additional attributes which will evaluated as a variable assignment. Note - the named variable of the correct type must already exist. Maybe at some point the code generator will keep track of declared variables in scope and create new 'var's where appropriate. I think I'll add a trac item for that.

<viewdata currentProductId="int"/>
<var styles="new [] {'even', 'odd'}" isCurrent="false">
  <for each="var product in Products" isCurrent="product.Id==currentProductId">
    <p class="highlighted?{isCurrent} ${styles[productIndex%2]}">${Html.Encode(product.Name)}</p>
  </for>
</var>

Iteration with the each attribute

You may also add the each attribute to any plain element.

<table>
  <tr>
    <td>Name</td>
    <td>Type</td>
  </tr>
  <tr each='var user in users'>
    <td>${user.Name}</td>
    <td>${user.UserType}</td>
  </tr>
</table>

If you do that other attributes are not treated as variable assignment, but they may contain ${expression} evaluation.

<table>
  <tr>
    <td>Name</td>
    <td>Type</td>
  </tr>
  <var classes="new [] {'even','odd'}">
    <tr each="var user in users" class="${classes[userIndex%2]}">
      <td>${userIndex}) ${user.Name}</td>
      <td>${user.UserType}</td>
    </tr>
  </var>
</table>

Using namespace

You can add using statements to avoid typing out fully qualified type names, and to make extension methods to helpers available.

<use namespace="System.Collections.Generic"/>
<use namespace="System.Web.Mvc"/>
<viewdata Names="IList[[string]]"/>

The use namespace elements may appear anywhere in the view, master, or partial template files. The namespaces are de-duplicated so it's safe to include them where they're needed in views and partials if you prefer even though the same namespace could appear several times.

Inline code

Sometimes, when push comes to shove, you have a situation where you're not writing output and there isn't a markup construct for what you want to do. As a last resort you can produce code directly in-place in the generated class.

This takes two forms. An aspx-like <%statement%> version is available as well as a #statement variation.

<test if="user.IsLoggedIn()">
  <p>Hello, ${user.Name}.</p>
  <else if="user.HasValidTrialSession()"/>
  <p>Hello, Valued Future Customer.</p>
  <else/>
  <p>Hello, er... you.</p>
  # System.Diagnostic.Trace.WriteLine("Unexpected anon user.");
  # if (System.Diagnostic.Debugger.IsAttached)
  #   System.Diagnostic.Debugger.Break();
</test>

Declaring Macros

When it comes right down to it a helper method is a function that takes arguments and returns a string. A Spark <macro> produces something which is much the same: a function that takes arguments and returns a string.

Because a macro is declared in a the spark template it's contents may contain any literal html, rendering of partial files, expression evaluation, etc. All of the output from this is captured as the simple return value. This enables you to invoke a <macro name="foo"> using ${foo()} or <%=foo()%> from anywhere in your template files.

<viewdata errorMessage="string" />
 
<macro name="ShowError" caption="string" message="string">
<div class="message error">
  <h3>${H(caption)}</h3>
  <div>${message}</div>
  <% Logger.Warn(caption); %> <!-- this is a MR example. asp.net mvc would use different logging -->
</div>
</macro>
 
<h2>Place Order</h2>
<test if="!string.IsNullOrEmpty(errorMessage)">
  ${ShowError("Failed to place order", errorMessage)}
</test>
 
<!-- form here, field validation messages, etc. -->

The following method generated code is the result

string ShowError(string caption, string message)
{
  using(OutputScope(new System.IO.StringWriter())) 
  { 
    Output.Write("\r\n  <div class=\"message error\">\r\n    <h3>");
    Output.Write(H(caption));
    Output.Write("</h3>\r\n    <div>");
    Output.Write(message);
    Output.Write("</div>\r\n    ");
    Logger.Warn(caption);
    Output.Write("  </div>\r\n");
    return Output.ToString(); 
  }
}
 
//... and in the RenderViewContent method
Output.Write("\r\n<h2>Place Order</h2>\r\n");
if (!string.IsNullOrEmpty(errorMessage)) 
{
  Output.Write(ShowError("Failed to place order", errorMessage));
} // if (!string.IsNullOrEmpty(errorMessage))
Output.Write("\r\n\r\n<!-- form here, field validation messages, etc. -->\r\n\r\n");

A macro may also call itself recursively, which is convenient for producing something like a threaded comment list or a nested ul/ul/li tree structure.