Spark Documentation

What is Spark

Spark is a view engine for Asp.Net Mvc and Castle Project MonoRail frameworks. The idea is to allow the html to dominate the flow and the code to fit seamlessly.

For example,

<ul>
  <li each='var p in ViewData.Model.Products'>
    ${p.Name} !{Html.ActionLink[[ProductController]](c=>c.Edit(p.Id), "Edit")}
  </li>  
</ul>

The full csharp language is available in a way that doesn't interfere with the harmony and balance of the markup. The view template files produced compiled classes.

Needs documentation

Configuring Spark

Adding to Castle MonoRail

In the web application add references to the Castle.MonoRail.Views.Spark and Spark assemblies.

In the <monorail> section configure the spark view engine.

<monorail>
  <viewEngines viewPathRoot="Views">
    <add type="Castle.MonoRail.Views.Spark.SparkViewFactory, Castle.MonoRail.Views.Spark"/>
  </viewEngines>
</monorail>

That's about it really.

Adding to Asp.Net MVC

View engines are no managed by the controller. This section is now correct for Preview 5

The simplest way to use Spark from an Asp.Net MVC web application is to register an instance of the SparkViewFactory inn your Global Application_Start function at the same point you would be registering routes and such.

protected void Application_Start(object sender, EventArgs e)
{
    ViewEngines.Engines.Add(new SparkViewFactory());
}

This is also an excellent place to provide any other non-default services to the view factory. Such as an IViewFolder that reads from embedded resources instead of the file system, or an IViewActivatorFactory which can take over the responsibility of instantiating the compiled view types.

todo - document use of IoC for this

Spark settings in config file

After the Spark view engine is integrated into your MVC, MonoRail, or other application there are some additional settings you may want to configure. One way to do this is with a <spark> config file section.

<configSections>
  <section name="spark" type="Spark.Configuration.SparkSectionHandler, Spark"/>
</configSections>
<spark>
  <compilation debug="true|false" defaultLanguage="Default|CSharp|VisualBasic">
    <assemblies>
      <add assembly="YourAssemblyName" />
      <add assembly="YourOtherStrongAssemblyName, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
  </compilation>
  <pages automaticEncoding="true|false" pageBaseType="Your.NonDefault.BaseSparkView" prefix="{optional string}">
    <namespaces>
      <add namespace="System"/>
      <add namespace="System.Collections.Generic"/>
      <add namespace="System.Linq"/>
      <add namespace="System.Web.Mvc"/>
    </namespaces>
    <resources>
      <add match="/content/css" location="http://www.yourcdnprovider.com/youraccount/allstyles/css"/>
      <add match="/content/js" location="http://www.yourcdnprovider.com/youraccount/appname/js"/>
    </resources>
  </pages>
</spark>

The defaultLanguage may be specified if you are using a language like VisualBasic. It's not necessary for nearly any other case.

Additional assemblies and namespaces can be referenced with <use assembly="..." namespace="..." /> in view, layout, and partial files. All assemblies loaded in the app domain are automatically referenced, there may be times you want to add references explicitly for assemblies that aren't yet loaded.

The default page base type, MvcContrib.SparkViewEngine.SparkView or Castle.MonoRail.Views.Spark.SparkView, will be used if one isn't specified. If you do provide a class as the page base type you'll probably inherit from the default base class.

Automatic HTML encoding of output may be enabled. This will result in every instance of ${expr} output passing through the intrinsic HTML encoding method H(). The syntax !{expr} is used in this case for output which should be sent unencoded.

An optional prefix may be selected, which must then be used on all Spark elements and attributes in your template files. The prefix should not be set here if you're defining a prefix in the templates with xmlns attributes.

The resources section may be used to control the result of ~/ style urls. For example, <script src="~/content/js/foo.js"/> will normally appear as a ${SiteRoot} prefixed url but the match for "/content/js" above will make that url appear as the given location + "/foo.js". The assumption for high-volume sites is you would use the normal urls for development and map them to a static file server or to a cdn provider in production.

Spark settings in code

Another way to provide these settings to the spark engine is to provide an ISparkSettings instance to the constructor.

protected void Application_Start(object sender, EventArgs e)
{
    var settings = new SparkSettings()
        .SetDebug(true)
        .SetPageBaseType("Your.NonDefault.BaseSparkView")
        .AddAssembly("YourAssembly")
        .AddNamespace("System")
        .AddNamespace("System.Collections.Generic")
        .AddNamespace("System.Linq")
        .AddNamespace("System.Web.Mvc");
 
    ViewEngines.Engines.Add(new SparkViewFactory(settings));
}

General Syntax

Basic markup

A view file has basic html markup that can appear pretty much as you'd expect.

<div>
  <p>Hello <span>world</span>!</p>
</div>

Including code

CSharp code may be used to produce output. Any helper methods, object properties, or expression that evaluates to a non-void value can be used.

<p>${"Hello world"}</p>
<p>${Guid.NewGuid().ToString("n")}</p>

Code will also appear in special attributes or elements like "if", "else", and "var". These types of areas will never require ${} or <%= %> notation.

<var names="new [] {'alpha', 'beta', 'gamma'}"/>
<for each="var name in names">
  <test if="name == 'beta'">
    <p>beta is my favorite.</p>
    <else/>
    <p>${name} is okay too I suppose. 
  </test>
</for>

However if you use code to produce a normal attribute's value, that is not a special attribute does require the code-output notation.

<viewdata currentProduct="Product"/>
<p>${currentProduct.Name} <a href="/Product/Edit/${currentProduct.Id}">Edit</a></p>

Special characters in code

The entire functionality of the csharp language is available. As always, please use responsibly. :) Be sure to put business logic in your controller's actions and write unit test for it.

There are a small number of characters that are parsed in a special way to avoid simple mechanical problems you can encounter in a standard xml editor.

The sequences [[ and ]] are parsed as if they are < and >. This can be convenient when you're using generic classes or methods from a spark file, to avoid the editor's tendency to add a closing elements for you. The [[ and ]] will not be changed if they occur inside a string literal.

String literals may be expressed in 'single quotes' in addition to "double quotes". This is again for convenience when using expressions inside attributes values that are contained in double quotes. If you do need to use a single char constant in an expression, using an explicit type-cast (char)'x' will avoid having the string changed to "x".

<viewdata products="IList[[Products]]"/>
<var styles="new[] {'even', 'odd'}"/>

Spark element prefix with config

By default, Spark tags are unqualified. All of the examples you'll see are written that way. That can cause a problem if Spark is being used to create xml rather than xhtml. One of the ways to disambiguate spark xml from output xml is to set a prefix for your application.

<spark>
  <pages prefix="s"/>
</spark>

Or

var settings = new SparkSettings()
    .SetPrefix("s");

At which point all of the native spark elements and attributes must include the s: prefix to be recognized. None of the built-in prefixes macro:, content:, use:, render:, and section: are affected by the use of the prefix setting.

<div>
  <s:viewdata user="UserInfo"/>
  <p s:if="user != null">${user.Name}</p>
  <s:LoginForm/>
</div>

Spark element prefix with xmlns namespaces

Another way to disambiguate the markup is to declare xmlns attributes, which will result in well-formed and valid xml. All of the Spark namespace Uris begin with http://sparkviewengine.com/. If a template file contains a Spark xmlns attribute then the other built-in prefixes (macro:, etc.) will only function if their namespace Uri's are also declared.

Default prefixNamespace Uri
none - spark tags and partial fileshttp://sparkviewengine/
content:http://sparkviewengine/content/
use:http://sparkviewengine/use/
macro:http://sparkviewengine/macro/
section:http://sparkviewengine/section/
render:http://sparkviewengine/render/

You can use the defaults or specify any prefix you choose. Once an xmlns attribute is defined only the value on the namespace Uri is significant.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:s="http://sparkviewengine.com/"
      xmlns:fn="http://sparkviewengine.com/macro/">
<body>
  <fn:ShowNames favorite="string">
    <s:var names="new [] {'alpha', 'beta', 'gamma'}"/>
    <s:for each="var name in names">
      <s:test if="name == favorite">
        <p>${favorite} is my favorite.</p>
        <s:else/>
        <p>${name} is okay too I suppose.
      </s:test>
    </s:for>
  </fn:ShowNames>
  ${ShowNames("beta")}
  ${ShowNames("gamma")}
</body>
</html>

Variables

Declaring local variables

The var element is a quick and easy way to declare local variables. The default behavior is to create a local of automatic type "var" which is initialized inline.

For example, the following:

<var styles="new [] {'odd', 'even'}" i="0"/>

Produces the following:

var styles = new [] {"odd", "even"};
var i = 0;

The var element has an optional type attribute which can be used to provide an explicit type used instead of the automatic type syntax "var".

<var foo="null" bar="null" type="string"/>

Will declare variables of type string instead of var.

string foo = null;
string bar = null;

Finally if the var element is not declared as a self-closing tag the scope of the variables will be limited. For example:

<div>
  <var styles="new [] {'even', 'odd'}" i="0">
    <p>${i} is ${styles[i%2]}</p>
  </var>
  <set i="5"/> <!-- compiler error - variable i is out of scope -->
</div>

will result in:

Output.Write("<div>");
{
var styles=new[] {"even", "odd"};
var i = 0;
Output.Write("<p>");
Output.Write(i);
Output.Write(" is ");
Output.Write(styles[i%2]);
Output.Write("</p>");
}
Output.Write("</div>");
i = 5;

In this case the local variables are only available inside the element. This can be convenient to scope explicitly.

Declaring global variables

Local variables have a limited scope. They're declared in the generated class right in line in the methods that are generating output. Globals on the other hand can be declared at any location in the view, master, or partial template files and the value can be used and assigned at any point.

<h2>${Title}</h2>
<global Title="'My Default Title'"/>

Global variables are actually implemented as field members of the view class. So the above example would contain object Title="My Default Title"; which is available to from any method. You may also use provide a type attribute to avoid using the default field type object. Multiple variables may be defined in the same element.

<global type="string" Foo="'Hello'" Bar="'World'" />

Setting local and global values

The ${expression} and <%=expression%> syntax is only useful for producing a value to send to output. For local and global variable assignment the set element may be used.

Using global for title

<html>
  <head>
    <global type='string' Title='"Site Name"'/>
    <title>${Title}</title>
  </head>
  <body>
    <div><use content="view"/></div>
  </body>
</html>

View that sets the title

<set Title='product.Name + " - " + Title'/>
<!-- or -->
<set Title='string.Format("{0} - {1}", product.Name, Title)'/>

Using view data

A ViewData property is available as an object dictionary on the base class so syntax like ${ViewData["blah"]} is valid. There is an easier option however which adds a strongly typed property accessor for a view data member.

<viewdata 
  Caption="string" 
  Products="System.Collections.Generic.IList[[MyApp.Models.Product]]"/>
<div>
  <h3>${Caption}</h3>
  <div  each="var product in Products">
    <h4>${product.Name}</h4>
    <div>${product.Description}</div>
  </div>
</div>

The viewdata element may occur anywhere in the view or master file actually. The net result is the same which is to add something like the following to the generated class.

string Caption 
{get {return (string)ViewData["Caption"];}}
 
System.Collections.Generic.IList<MyApp.Models.Product> Products 
{get {return (System.Collections.Generic.IList<MyApp.Models.Product>)ViewData["Products"];}}

In this case the [[ and ]] are replaced with < and > because of the difficulty xml-centric editors have with certain characters appearing in attributes.

Using view data with a typed model

The viewdata element may have a model attribute in the view or master file.

<viewdata model="MyApp.Models.Catalog"/>
<p>(${ViewData.Model.CatalogID}) ${ViewData.Model.Name} </p>

The net effect is to have the model attribute used as the TModel parameter for the ViewDataDictionary.

The model attribute can be used in conjunction with the other viewdata variable declaration. For example if your master template uses a typecast named viewdata argument to access a member variable, like <viewdata Title="string"/>, your views can still use a specific <viewdata model="typename"/> if the type has a string Title property.

<viewdata Title="string"/>
<title>${Html.Encode(Title ?? "My Sample MVC Application")}</title>

The above example enables you to add a string Title property to any class used as a TModel, or as an automatic class new {Title = "hello"}, or as a dictionary entry in an untyped view data dictionary.

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.

Master Layouts

Preface

This entire subject is ASP.NET MVC specific. MonoRail has it's own mechanism for selecting layout files, and Spark works in that context as expected.

How master-layout files work

When a view is rendered with a master layout template selected Spark renders the view in a multiple passes. The first pass will render the contents of the view template from the top down. The output of that pass is captured in a content variable named "view".

The second pass will render the contents of the master template from the top down. The master template will contain <use content="view"/> or <use:view/> at the location where the view content should appear.

Application.spark

<html>
  <head>
    <title>${Title}</title>
  </head>
  <body>
    <div id="header">dot dot dot</div>
    <div id="pageContent"><use content="view"/></div>
    <div id="footer">dot dot dot</div>
  </body>
</html>

From the master template's perpective the "view" is just a piece of named content.

Selecting Layout

There are several ways to select a which master layout file should be used. The following are the ways a template may be selected, from weakest to strongest.

This is the most general-purpose way to have a site-wide master template. It will not be used if the controller returns a PartialView().

For example if you have an AccountController you could have a Views/Layouts/Account.spark file which is used on that controller, but all other controllers use the Views/Layouts/Application.spark template.

This gives the selection of layout to the controller, which some people may believe isn't necessarily a concern for the controller. What'cha gonna do? If this is present it will override the first two conventions.

This is actually the strongest mechanism available for wrapping a view in a layout file. It will override the conventional forms of master selection, and it will cause the name of the master in the View() ActionResult to be ignored if it's present.

Interestingly, it may also be used in the layout files themselves to establish three or more pass rendering.

Three pass rendering

Three pass rendering can be used to address a common problem. The cause isn't always obvious, especially if you're new to the named-content top-down rendering technique.

The problem is this. Let's say you're using a named content section, like "head" or "nav", at the top of your master layout file. Then you add some partials in the sidebar or other places in the layout that add script and stylesheet references to the "head" content. The problem you'll encounter is that the "head" is actually written to the output stream before those bits further down the layout execute and add those resources to the named content.

Views/Layouts/Application.spark

<html>
  <head>
    <use content="head"/>
  </head>
  <body>
    <Header/>
    <use content="view"/>
    <Sidebar/>
    <Footer/>
    <use content="tail"/>
  </body>
</html>

Views/Shared/_Sidebar.spark

<content name="head">
  <script src="~/content/js/jquery-ui-accordian.js" once="jquery-accordian"></script>
</content>
<div id="sidebar">etc</div>

One way to use three-pass rendering to solve this problem would be to create a super-layout named Html.spark, and for the master file Application.spark to wrap itself in that layout. Note that from the Html.spark file's perspective the entire output of the Application.spark, and the view which it encloses, is contained in the "view" named content.

Views/Layouts/Html.spark

<html>
  <head>
    <use content="head"/>
  </head>
  <body>
    <use content="view"/>
    <use content="tail"/>
  </body>
</html>

Views/Layouts/Application.spark

<use master="Html"/>
 
<Header/>
<use content="view"/>
<Sidebar/>
<Footer/>

Organizing Content

Master-View relationship

The view contents are rendered first, followed by the master contents. The master template includes the view at the appropriate location with ${Content["view"]}, or <use content="view"/>, or <use:view/>.

<html>
  <head>
    <title>${Title}</title>
  </head>
  <body>
    <div id="header">dot dot dot</div>
    <div id="pageContent"><use content="view"/></div>
    <div id="footer">dot dot dot</div>
  </body>
</html>

Named content sections

Defining additional number of content sections or modules is one way you can create extensible blocks or regions in your layout. It's also a convenient way to enable a view to include additional css and js references in the head section.

This example shows a master layout which provides the ability for the views to add script and style to the head element, and add sections to a sidebar container.

Making named content appear in output

<html>
  <head>
    <title>${Title}</title>
    <use content="head"/>
  </head>
  <body>
    <div id="header">dot dot dot</div>
    <div id="sidebar"><use content="sidebar"/></div>
    <div id="pageContent"><use content="view"/></div>
    <div id="footer">dot dot dot</div>
  </body>
</html>

Adding information to named content locations

<p>this goes in the pageContent</p>
 
<content name="sidebar">
  <h3>Search</h3>
  <p>This is added to the sidebar</p>
<content>
 
<content name="head">
  <script type="text/javascript" src="yadda/file.js"></script>
<content>
 
<content name="sidebar">
  <h3>See also</h3>
  <p>This is also added to the sidebar</p>
<content>

The implementation details of this are very straightforward. The generated class is appending values to the current textwriter. When a element is encountered it changes which textwriter is current. Later when a <use content="foo"/> or ${Content["foo"]} is encountered the accumulated text in the named content is written to the textwriter that is current at the time.

There is one intrinsic content section named "view". It will contain the output of the previous level when rendering view/master. In MonoRail there can be several layouts named, and the section named "view" will always contain the total result of the previous levels.

You can also use the prefixes as an alias for <content name="foo"> and <use content="foo">. Those would be content:foo and use:foo respectively.

<content:message>
  <p>This is a message</p>
</content:message>
 
<content:message>
  <p>This is another message</p>
</content:message>
 
<div class="messages" if="Content.ContainsKey('message')">
  <use:message/>
</div>

Parsing and rendering partial files

You can include and render a partial file at a particular location with the following syntax:

viewfile

<use file="mypartial"/>

This will look for a mypartial.xml file in the same directory as the view or in the Shared directory.

You can also declare local variables as attributes of the "use" element. Those variables, and anything else that was in context, can be used within the scope of the partial as local variables.

viewfile

<use file="mypartial" caption="product.Name"/>

mypartial.xml

<div>
<h3>${caption}</h3>
</div>

Implicit partial rendering

Finally, if your partial file starts with an underscore character the rest of the file name can be used as a new special element. This is nothing more than <specialname/> being used as a shortcut for but it sure looks cool.

viewfile

<var styles='new[] {"even", "odd"}' i='0'>
  <for each='var product in Products' i='i+1'>
    <ProductSummary cssclass="styles[i%2]" number="i"/>
  </for>
</var>

_ProductSummary.xml

<div class="${cssclass}">
<p>${number}) ${product.Name}</p>
</div>

Partial files starting with an underscore will be used from the view directory of the controller and from the "Shared" view directory.

Importing files

Many of the elements like <global>, <viewdata>, <macro name="">, <use namespace="">, and <use assembly=""> have an effect on the generated view class but do not produce output in-line.

You can remove a lot of these declarations from view, partial, and layout files by moving them into a spark file which you then import.

Shared\CommonMacros.spark

General-purpose references
<use namespace="System"/>
<use namespace="System.Collections.Generic"/>
<use assembly="MyWebApp"/>
 
These are null if they're not in the viewdatadictionary or propertybag
<viewdata warning="string" error="string"/>
 
This macro writes out a warning or error if it's in the viewdata.
<macro:WarningOrError>
  <div class="msgbox warning" if="!string.IsNullOrEmpty(warning)">
    ${H(warning)}
  </div>
  <div class="msgbox error" if="!string.IsNullOrEmpty(error)">
    ${H(error)}
  </div>
</macro:WarningOrError>

SomeView.spark

  <use import="CommonMacros"/>
  ${WarningOrError()}

Import files are much like partial files, except they will not generate rendering code at the points where the <use import=""> occurs. Any text or markup in the imported file, other than the class-level declarations, can be used more or less like comment that won't add a single thing to the generated class or the html output. You can also import the same file from any number of views, partials, and layouts and it's contents will only be used once.

You may also place a _global.spark in a controller's view folder, or the layouts view folder, and that file will be automatically imported whenever a view or layout template is used from that location. Another file shared\_global.spark can be created which will be imported once into every single view that's compiled.

Including files

Another way to manage content is with the <include href=""> element. The implementation of this element is based on a subset of the xinclude specification. See also O'Reilly Using XInclude and MSDN Combining XML Documents with XInclude.

The include and fallback elements are both supported. The include element may have href and parse attributes. The value href must be a relative path, may contain ".." style parent paths, and must include the target file's extension. The dotted parent paths may not go beyond the root of the IViewFolder.

<h2>Chapter One</h2>
<include href="../lib/chap01.xml"/>
<hr/>
<h3>Disclaimer</h3>
<include href="../lib/legal.txt" parse="text">
  <fallback>It appears the lawyers have nothing to say.</fallback>
</include>

You may also use explicit namespaces. Note: declaring namespaces with xmlns attibutes is all-or-nothing. If you declare the XInclude namespace in a file you must also declare the Spark namespace and use element prefixes.

<div class="shimmer" xmlns:s="http://sparkviewengine.com/" xmlns:xi="http://www.w3.org/2001/XInclude">
<p>Let's add a stylesheet to the header</p>
<s:content name="head">
  <style type="text/css">
    <xi:include href="../lib/effects.css" parse="text"/>
  </style>
</s:content>
</div>

When a file is including another file it happens before any significant processing takes place. As far as the Spark view engine is concerned the contents of the target file may as well have been copy-and-pasted right at the location of the include element. This can have some advantages, but be cautions about duplicate macro declarations or circular include references.

Output Caching

Requires Spark version 1.1

Caching part of a template

Output from a Spark template may be cached by wrapping it in a <cache> element. Let's assuming you have a ShowConversionTable macro that does some expensive data acquisition through a service class.

<viewdata Conversion="IConversionRateService"/>
<h1>A cached section</h1>
<p>Current conversion rate ${Conversion.CurrentRate}</p>
!{ShowConversionTable(Conversion)}

You could cache the results of this html output by wrapping the section.

<viewdata Conversion="IConversionRateService"/>
<h1>A cached section</h1>
<cache>
  <p>Current conversion rate ${Conversion.CurrentRate}</p>
  !{ShowConversionTable(Conversion)}
</cache>

The second and subsequent times this template renders it will reuse the output from the first time it ran. This output would be stored in the ASP.NET cache the first time the cached segment had run.

Note! It's generally considered a bad practice for the view to acquire the data or model which it is exposing. In further examples below we will see a way to keep data acquisition in your controller's action in a way that caching will still avoid the data acquisition costs.

Caching that varies by parameters

Frequently you'll want to cache things which don't show the same data on every request, or don't appear the same to all users. For example, let's say our conversion table has a parameter to determine the currency to use as the baseline denomination. In that case you would provide the additional key values that would make the output to cache unique.

<viewdata 
    Conversion="IConversionRateService" 
    Baseline="string"/>
<h1>A cached section</h1>
<cache key="Baseline">
  <p>Current rate over Euro ${Conversion.GetCurrentRate(Baseline, "EUR")}</p>
  !{ShowConversionTable(Conversion, Baseline)}
</cache>

Several parameters may be delimited by commas. This works because attribute value is used as a params object[] keys argument, so if commas are present it create a multiple-field key for the cache entry.

<viewdata 
    Conversion="IConversionRateService" 
    Baseline="string"/>
<h1>A cached section</h1>
<cache key="Baseline, CurrentUserProfile.CountryOfOrigin.Currency">
  <var userCurrency="CurrentUserProfile.CountryOfOrigin.Currency"/>
  <p>${Baseline} rate over ${userCurrency} ${Conversion.GetCurrentRate(Baseline, userCurrency)}</p>
  !{ShowConversionTable(Conversion, Baseline)}
</cache>

Giving cache a specific time-to-live

In the first example the same information could be returned for the life of the web host's app domain. In a lot of cases there's a certain point where you would consider the age of the data to be acceptable. To ensure the same output will only be used for five minutes from when it was originally capured you would provide an expires attribute with a DateTime coordinate, preferable in Utc.

<viewdata 
    Conversion="IConversionRateService" 
    Baseline="string"/>
<h1>A cached section</h1>
<cache key="Baseline" expires="DateTime.UtcNow.AddMinutes(5)">
  <p>Current rate over Euro ${Conversion.GetCurrentRate(Baseline, "EUR")}</p>
  !{ShowConversionTable(Conversion, Baseline)}
</cache>

The expires attribute may be used with or without a key. In this example having both present would be correct.

Expiring when unused over time

You may have part of a view that isn't particularly time sensitive, but you don't want the entries to remain in memory when their key value hasn't been used for a while. That could be a case to consider using a TimeSpan instead of a DateTime for the expires parameter.

<h1>Product Details</h1>
<cache key="productId" expires="TimeSpan.FromMinutes(20)">
  <p>${product.Name}</p>
  <div class="productimage">${ShowProductImage(product.Id)}</div>
  <p>${product.Description}</p>
  <div class="orderhistory">
    ${ShowThirtyDayOrderingHistory(product.Id)}
  </div>
</cache>

For convenience a numeric value will be treated as the value of a TimeSpan in seconds.

<cache expires="600">
  <p>
    This will remain cached until you stop 
    using the page for ten minutes.
  </p>
</cache>

Note! This example is again implying the view is acquiring data. A preferred method would be to have this information represented by a model added to the ViewData by the controller's action. Next we will look at avoiding that problem.

Using ValueHolder for clean views

As often as possible you will want to ensure the controller's action is gathering all of the model information needed for the view to render itself. This causes a problem when you are attempting to utilize caching, because it's often the gathering of the model's data where the real cost occurs.

One way to ensure caching will avoid this cost is to have the view gather the data needed for the portion of the template which is cached. That way then a cache-hit occurs you also avoid the cost of making WCF or database calls to acquire the data.

A better way however would be to take advantage of a class like the ValueHolder provided by the spark.dll assembly. It's a class with a generic typed Value property, and the constructor accepts a lambda expression which will be called at most once the first time the value is used. The lambda may also reference local variables and action arguments even though it's technically being called when the view result is executing.

public ActionResult Details(int id)
{
    var data = new NorthwindDataContext();
 
    ViewData["employeeId"] = id;
 
    ViewData["employee"] = ValueHolder.For(
        () => data.Employees.Single(x => x.EmployeeID == id));
 
    return View();
}

One of the ways you could use this in a view is like this. Because the value property is only used in a cached section, the database not called if the same employee is viewed within three minutes.

<viewdata 
    employeeId="int" 
    employee="Spark.ValueHolder[[Employee]]"/>
<cache key="employeeId" expires="180">
  <p>${employee.Value.Name}</p>
  !{ShowSalesHistory(employee.Value)}
</cache>

There's also an interesting trick with the <viewdata> element you can use if you want to take advantage of Eval's ability to access properties directly. In this case it will add a public Employee employee { get } property which will return (Employee)ViewData.Eval("employee.Value");.

The net result is that you can use the property employee directly without adding the .Value onto it.

<viewdata 
    employeeId="int" 
    employee.Value="Employee employee"/>
<cache key="employeeId" expires="180">
  <p>${employee.Name}</p>
  !{ShowSalesHistory(employee)}
</cache>

You can also create a ValueHolder with a typed Key property, and use other class methods to acquire the data instead of a lambda expression.

public ActionResult Details(int id)
{
    ViewData["employee"] = ValueHolder.For<int, Employee>(id, FetchEmployee);
 
    return View();
}
 
private static Employee FetchEmployee(int employeeID)
{
    var data = new NorthwindDataContext();
    return data.Employees.Single(x => x.EmployeeID == employeeID);
}

And you can use this key property from the ValueHolder<TKey, TValue> class like this.

<viewdata employee="Spark.ValueHolder[[int, Employee]]"/>
<cache key="employee.Key" expires="180">
  <p>${employee.Value.Name}</p>
  !{ShowSalesHistory(employee.Value)}
</cache>

Or you could use the Eval trick again like this.

<viewdata 
    employee.Key="int employeeId"
    employee.Value="Employee employee"/>
<cache key="employeeId" expires="180">
  <p>${employee.Name}</p>
  !{ShowSalesHistory(employee)}
</cache>

Signaling dependent data changed

Expiring the cached output when it reaches a certain age, or when unused for a certain period, always involves a trade-off. With a shorted expiration you're losing cache efficiency, because it's being re-generated more frequently, and with longer expiration you're accepting the fact the web site will return information that's out of date for a certain period. That can be especially noticeable if your web site is on a server farm, because the different nodes may return different cached results if you refresh a page repeatedly.

To have ultimate control over your cached data you may provide an ICacheSignal that will fire an event when the data changes. Say you have a component that has a RegionSalesBuilder.GetFigures(int regionId) method, and you also implement a RegionSalesWatcher.GetSignalForRegion(int regionId) that will return an ICacheSignal.

public ActionResult SalesFigures(int id)
{
  var worker = new RegionSalesBuilder();
  var watcher = new RegionSalesWatcher();
  return View(new {
    regionId = id,
    figures = ValueHolder.For<int, RegionSales>(id, worker.GetFigures),
    salesChanged = watcher.GetSignalForRegion(id),
    });    
}

<viewdata 
  regionId="int" 
  figures="ValueHolder[[RegionSales]]" 
  salesChanged="ICacheSignal"/>
<cache key="regionId" signal="salesChanged">
  All of the ${figures.Value.Stuff} shown here
</cache>

The ICacheSignal contains only a single event named Changed, and you may implement that interface in any way you would like. For example you can use a single instance of that object for your entire database, or you could have a distinct instance for each piece of data you want to expire individually.

There is also a CacheSignal class you may use which implements ICacheSignal. It has a public method named FireChanged you may call at any time to expire all cached output which used that instance as a signal.

public class RegionSalesWatcher
{
  static CacheSignal _allDataSignal = new CacheSignal();
 
  ICacheSignal GetSignalForRegion(int regionId)
  {
    return _allDataSignal;
  }
 
  public static void AppStarted()
  {
    //obvious pseudo-code
    var conn = opendatabase();
    conn.selecttableswithnotification(OnTablesChanged);
  }
 
  static void OnTablesChanged()
  {
    _allDataSignal.FireChanged();
  } 
}

Yet way of using CacheSignal would be as a base class. In this case you would override the Enabled and Disabled methods to build up and tear down the change watching mechanism, and call the base FireChanged when a change occurs.

public class FileWatcherSignal : ChangeSignal
{
  string _path;
  public FileWatcherSignal(string path)
  {
    _path = path;
  }
 
  protected override void Enable()
  { 
     pseudo_code_start_file_watcher(FileWatcher_ChangedEventHandler);
  }
 
  protected override void Disable()
  { 
     pseudo_code_stop_file_watcher();
  }
 
  void FileWatcher_ChangedEventHandler()
  {
    FireChanged();
  }
}

Although Spark provides support for signaling cache expiration, and the ICacheSignal interface and CacheSignal class, there are no built-in implementations of file or database change notifiers. The assumption is that databases and their access libraries are so varied there's a vanishingly small change canned implementations would be useful.

Or how does this sound... Send me a patch and I'll add them. :)

Using cache with xml attributes

The <cache> element can be implied by adding certain attributes to other elements.

<div cache.key="employeeId" 
     cache.expires="TimeSpan.FromMinutes(20)" 
     cache.signal="MyApp.Signals.Employees">
  This div element is cached.
</div>

The attribute cache="xxx" is an alias for cache.key="xxx"

<div cache="employeeId" cache.expires="180">
  <p>This has a 3 minute sliding expiration per employee id</p>
  <p>${employeeId}</p>
</div>

Precompiling Views

You can create a precompiled assembly with view classes at a high level with the a batch descriptor and a SparkViewFactory, or at a low level with an array of view descriptors and a SparkViewEngine.

If you provide a high-level batch descriptor to the asp.net mvc or castle monorail SparkViewFactory it will be converted into an array of descriptors based on which view files exist. Those will then be passed to the SparkViewEngine.

Precompiling on-startup with the Asp.Net Mvc SparkViewFactory

        public static void PrecompileViews(ControllerBuilder builder)
        {
            var controllerFactory = (SparkControllerFactory)builder.GetControllerFactory();
            var viewFactory = new SparkViewFactory(controllerFactory.Settings);
            var batch = new SparkBatchDescriptor();
            batch
                .For<HomeController>()
                .For<ProductsController>();
            viewFactory.Precompile(batch);
        }

A static method like this would be called from Global Application_Start. The tricky part is getting your hands on the instance of the view factory.

Precompiling on-startup with the Castle MonoRail SparkViewFactory

//todo - about the same. just need sample code

Precompiling on-startup with the low-level SparkViewEngine

If you create an instance of the SparkViewEngine you can call BatchCompilation directly. It takes the name of the target assembly, which should have the .dll extension, and a list of SparkViewDescriptor which have been filled in. The SparkViewEngine will need to have the ViewFolder property set.

            var engine = new SparkViewEngine(settings)
                             {
                                 ViewFolder = new FileSystemViewFolder(viewsLocation)
                             };
 
            engine.BatchCompilation(targetPath, Global.AllKnownDescriptors());

Using precompilation in a unit test

With Asp.Net Mvc the view factory is fairly easy to create when you need it. The tricky part can be making sure the path to the views folder is correct. You could pass in settings or assume that the spark section in the app.config in your test project is correct.

            var viewFactory = new SparkViewFactory(settings)
                                  {
                                      ViewSourceLoader = new FileSystemViewSourceLoader(@"......\NorthwindDemo\Views")
                                  };
 
            var batch = new SparkBatchDescriptor();
            batch.For<ProductsController>();
            viewFactory.Precompile(batch);

The return value of Precompile and BatchCompilation is the resulting loaded Assembly. You could continue to make assertions about the number of types in the file.

Precompiling bin-deployable assembly with installutil and with a post-build step

This one is probably the coolest one. Especially if you're developing a web app to deploy to a medium trust shared environment. You can find some examples of this in the Samples\AspNetMvc\PrecompiledViews project and Samples\DirectUsage\MediumTrustHosting project.

First: Add the following to the web.config. This will cause an exception to be thrown if your web app ever tries to compile a view at runtime, which will help avoid a situation where you work in development (because the view will generate on the fly) but not in production.

  <system.web>
    <trust level="Medium" />
    ...

Second: Add a new Installer Class item to your web project. It will have a grey designer surface. From the toolbox drag a new instance of either the MvcContrib.SparkViewEngine.Install.PrecompileInstaller or the Castle.MonoRail.Views.Spark.Install.PrecompileInstaller onto this surface.

(If it's not in the toolbox you may need to right-click one of the categories, say "Choose Items...", and Browse... to locate the assembly that contains the tool you want.)

Third: Select the precompileInstaller1 you just added. If you like you can provide a target assembly file name - by default it will add .Views.dll to your web app assembly name. Then choose the events in the properties (yellow lightning) and double-click "DescribeBatch". Provide the implementation, for example:

        private void precompileInstaller1_DescribeBatch(object sender, DescribeBatchEventArgs e)
        {
            e.Batch
                .For<HomeController>()
                .For<HomeController>().Layout("Ajax").Include("_Notification")
                .For<AccountController>();
        }

You could also add a static to Global and have this event handler call that static if you don't want to bury information like this in an event on a designer of an installer component. You could also then re-use that function from a unit test that compiles everything also.

Fourth and finally: Right-click the web project, choose properties, and change the Build Events. Add the following Post-build event command line:

%systemroot%\Microsoft.NET\Framework\v2.0.50727\installutil "$(TargetPath)"

Done! Now every time you hit Build, or F5-Debug/Run, or run unit tests from visual studio you'll automatically generate the precompiled views assembly in the bin directory. Plus, as icing on the cake, if there are any errors that occur in the installer they'll show up in the build output.

------ Build started: Project: PrecompiledViews, Configuration: Debug Any CPU ------
PrecompiledViews -> E:\Projects\spark\src\Samples\AspNetMvc\PrecompiledViews\bin\PrecompiledViews.dll
%systemroot%\Microsoft.NET\Framework\v2.0.50727\installutil "E:\Projects\spark\src\Samples\AspNetMvc\PrecompiledViews\bin\PrecompiledViews.dll"
Microsoft (R) .NET Framework Installation utility Version 2.0.50727.1433
Copyright (c) Microsoft Corporation.  All rights reserved.
...etc...
An exception occurred during the Install phase.
Spark.Compiler.CompilerException: Dynamic view compilation failed.
generated.cs(20,22): error CS0103: The name 'hi' does not exist in the current context

Loading a deployed precompiled assembly

When the app loads, usually in Global.Application_Start, you can load an existing precompiled view assembly instead of generating one. You would need to call SparkViewEngine's LoadCompilation(Assembly precompiled) method. It will enumerate all of the public types in the assembly to recreate and cache the appropriate SparkViewDescriptors. The array of descriptors which were loaded are returned.

        public static void LoadPrecompiledViews(SparkViewFactory factory)
        {
            factory.Engine.LoadBatchCompilation(Assembly.Load("MyWebApp.Views.dll"));
        }

As always, the tricky part can be getting your hands on an instance of the SparkViewFactory.

There is no dynamic recompilation of classes that are loaded in this way and changes to the spark files will have no effect. However the files must be available on disk at runtime. Sorry about that - but the different frameworks still needs to test for the view files' existence to know what view descriptor it should instantiate.

Configuring batch descriptors

The batch descriptor contains multiple entries and is built with a fluent interface. The target assembly name can be provided on the constructor, otherwise the assembly will be named after a guid and go to the temporary asp.net files directory.

Each call to .For<YourController>() starts a new rule entry. Within a rule you can call Layout, Include, and Exclude multiple times. Include and Exclude can have patterns that end with *. If the pattern ends with * it won't find things in "Shared". The other special case is the single character "*" which matches anything that doesn't start with an underscore.

batch
  .For<HomeController>
  .For<HomeController>.Layout("ajax").Include("_*")
  .For<ProductsController>.Include("Index").Include("List").Include("Detail")
  .For<AccountController>.Include("*").Exclude("TestMessage").Exclude("Blah");

If Include is not called for a rule the default is to include all views that don't start with a "_". If Layout is not called the default is to use the layout that would be used automatically appropriate for the framework in question. If you call .Layout multiple times it'll generate a class for each combination of layout and view. That's if you want to precompile several themes where the controller will specify the layout at runtime.

A call to layout can take an array of strings. This is not the same as calling Layout multiple times - rather it's to support MonoRail's ability to organize the layout into several layers.

View Locations

Spark file patterns

Spark will locate files using a very conventional pattern. These patterns will not change no matter which view folders are configured.

For controller views, the name of the controller and the name of the view are combined, like home\index.spark. If that file does not exist the name of the view is looked for in the Shared folder, like shared\index.spark.

For master layout templates, the name of the master will looked for in the Layouts folder, like layouts\application.spark. If that file does not exist it too will be looked for in the Shared folder, like shared\application.spark.

These patters will be looked for inside the view folder implementations. The view folders will never alter that part of the Spark file pattern, and the patterns won't affect the location of the view folders. This is very different from the behavior of the WebForms view engine which allows you to provide an array of customized virtual paths.

Sorry, but it was simply impossible to give the view engine full control of the entire path and remain compatible with the different frameworks and existing usage. The customized virtual paths are also incompatible with some of the specialized view folder sources.

Extending file patterns with descriptor filters

This is an ASP.NET MVC specific feature. In MonoRail the framework locates the view files and the paths are provided explicitly.

The SparkViewFactory uses a IDescriptorBuilder service to turn master, controller, and view names into a SparkViewDescriptor. The DefaultDescriptorBuilder provides a mechanism to add any number of IDescriptorFilter implementations to modify the potential locations of a view using information from the current request context.

There are three build-in implementations of IDescriptorFilter: AreaDescriptorFilter, LanguageDescriptorFilter, and ThemeDescriptorFilter. All three are optional, but the AreaDescriptorFilter is added by default. The other two require the web application to provide the mechanism that identifies a theme or language for a request.

Note: this descriptor filters only alter the search paths for views, layouts, and asp.net partial views. It does not change the search paths for _filename.spark partial files.

Here is example of adding a language filter. The LanguageDecriptorFilter is an abstract class, so you either need to inherit and implement or use the static method For which takes a lambda expression or method reference.

public static void RegisterViewEngine(ICollection<IViewEngine> engines)
{
    var services = SparkEngineStarter.CreateContainer();
    services.AddFilter(LanguageDescriptorFilter.For(GetSessionCulture));
    SparkEngineStarter.RegisterViewEngine(services);
}
public static string GetSessionCulture(ControllerContext controllerContext)
{
    return Convert.ToString(controllerContext.HttpContext.Session["culture"]);
}

AreaDescriptorFilter

The area filter by default detects an {area} RouteData.Value. It is added as leading folder.

For example, with Area:Sales, Controller:Foo, and View:Bar the locations:

become:

ThemeDescriptorFilter

The theme filter requires a lambda expression or method reference at a minimum (see above). The theme could be stored anywhere in the request context like a session variable, cookie, or user profile settings.

The filter will extend the potential locations for views by prefixing a Themes folder and the name of the theme to the potential locations. For example, with Theme:CleanBlue, Controller:Foo, and View:Bar the potential locations:

become:

LanguageDescriptorFilter

Like the theme filter, the language descriptor filter requires a lambda expression when it's registered.

This filter operates by inserting the language-locale, and just the language, before the extension on each potential location. For example, with Langauge:en-US, Controller:Foo, and View:Bar the potential locations:

become:

Custom IDescriptorFilter implementations

It should be very simple to implement a custom descriptor filter. The only two methods are ExtraParameters, which is called frequently to extract significant values for the filter from the current context, and PotentialLocations, which is called on a cache-miss to extend the list of paths where a given view template may exist.

As an example, this is the implementation of the AreaDescriptorFilter.

public class AreaDescriptorFilter : DescriptorFilterBase
{
    public override void ExtraParameters(
        ControllerContext context, 
        IDictionary<string, object> extra)
    {
        object value;
        if (context.RouteData.Values.TryGetValue("area", out value))
            extra["area"] = value;
    }
 
    public override IEnumerable<string> PotentialLocations(
        IEnumerable<string> locations, 
        IDictionary<string, object> extra)
    {
        string areaName;
        return TryGetString(extra, "area", out areaName)
                   ? locations.Select(x => Path.Combine(areaName, x)).Concat(locations)
                   : locations;
    }
}

Understanding IViewFolder and IViewFile

The Spark library declares two interfaces which provide the ability to locate view files and load their contents. There are very few methods and several implementations of typical view locations are provided, so in most cases the most you will need to do is provide additional configuration.

Default view folder locations

In a Castle MonoRail application the configured IViewSourceLoader is used by the default IViewFolder implementation. The root location of the default IViewSourceLoader is ~/Views and can be changed with the monorail/viewEngines/@viewPathRoot configuration attribute.

In an Asp.Net MVC applications a view folder based on hosting environment's VirtualPathProvider is used. The root location within this will be ~/Views.

The behavior of those abstractions can be controlled in ways that aren't documented here; custom VirtualPathProviders is a large topic all on it's own. Whatever you do to the framework to alter how files are loaded should work with Spark as expected.

What is documented on this page is how to bring in additional IViewFolder implementations to extend the way spark files can be located.

Adding a view folder to config

The <spark> config section accepts a <views> element which can be used to add additional view folder locations.

<spark>
  <views>
    <add name="{any-unique-name}" 
        folderType="FileSystem|EmbeddedResource|VirtualPathProvider|Custom"
        type="{name, assembly of IViewFolder type}"
        constuctor-param-names="values"
        subfolder="{optional subfolder to target}"/>
  </views>
</spark>

For example:

<spark>
  <views>
    <add name="stuff" folderType="EmbeddedResource" assembly="MyExtraStuff" resourcePath="MyExtraStuff.Views"/>
  </views>
</spark>

Understanding the subfolder parameter

The subfolder parameter will make the contents for a view folder appear at a specific location in the logical views directory. In other words you're adding files, but you're adding them at a subdirectory.

For example if you create a ~/Masters directory in your web app you could expose it in place with the following:

<spark>
  <views>
    <add name="morestuff" folderType="VirtualPathProvider" subfolder="layouts" virtualBaseDir="~/Masters"/>
  </views>
</spark>

Any path can be the target for adding additional files. If no subfolder is provided the contents are added at the root.

Visual Basic

Creating a new project

Assuming you have a Visual Basic ASP.NET MVC web application there are very few things you need to enable Spark. In the end it's very similar to the CSharp implementation.

First, add references to the following assemblies:

Then add the following minimal configuration to change Spark's default target language to Visual Basic:

<configuration>
  <configSections>
    ...
    <section name="spark" type="Spark.Configuration.SparkSectionHandler, Spark"/>
  </configSections>
  <spark>
    <compilation defaultLanguage="VisualBasic"/>
  </spark>
  ...
</configuration>

You'll also want to ensure the <system.codedom> configuration contains a definition of visualbasic which targets the v3.5 framework or higher.

Finally add the following to your Global.asax file:

    Shared Sub RegisterViewEngines(ByVal engines As ViewEngineCollection)
        SparkEngineStarter.RegisterViewEngine(engines)
    End Sub
 
    Sub Application_Start()
        ....
        RegisterViewEngines(ViewEngines.Engines)
    End Sub

Adding views

At this point everything about Spark works exactly as it does normally with the exception that the CLR syntax used is Visual Basic. Another place to check out VB syntax in Spark is located at Samples\AspNetMvc\VisualBasicViews.

#' this is a comment
 
<!-- Dim x = 4 + 5 -->
<var x="4 + 5"/>
 
<!-- Dim xx As Invoice = Nothing -->
<var xx="Nothing" type="Invoice" />
 
<!-- Note: single = used instead of == -->
<test if="x = 9">
 
  <!-- Note: VisualBasic does not require 'var' in foreach loop
  <!-- For Each prod in Products -->
  <for each="prod in Products">
  </for>
 
  <!-- and you can provide type in the loop with optional 'As' -->
  <for each="p As Product in Products">
  </for>
 
</test>
 
<!-- And the rest is about like you'd expect -->
#Using Html.BeginForm()
 
<!-- although the anonymous object syntax is a bit odd -->
 
!{Html.ActionLink("Show Order", "View", New With {.id = MyOrderId} ) }
 
#End Using

Iron Python

About Iron Python

Iron Python is a dynamic language runtime (DLR) based implementation of Python. Spark scripting is based on assemblies in the 2.0 Release Candidate 1 binary zip. Other good resources are the Python documentation and the Python site itself.

The support for the DLR is very Asp.Net MVC centric. If there's an interest in expanded Castle MonoRail support, please speak up in the discussion group.

Creating a new project

A standard Asp.Net MVC web application is always a great starting place. First, add references to the following assemblies:

And the following from the IronPython release which are available in the Spark bin\dependencies folder. If you have problems with the these files from Codeplex try the ones in from the Spark distribution. You have the "works on my machine" guarantee.

Yeah, I know. It's a lot of assemblies. You're going to love the next part though, adding the Spark view engine to an Asp.Net MVC web application has been simplified.

Add the following to your Global Application_Start method:

using Spark.Web.Mvc.Scripting;
...
protected void Application_Start(object sender, EventArgs e)
{
    SparkPythonEngineStarter.RegisterViewEngine();
}

This will create a view engine and add it to the engines collection.

The Spark engine starter also has several utility methods for more advanced initialization. You can, for example, provide a settings object from code. You can also use the ISparkServicesContainer to provide specific implementations of different services used by the Spark engine. For example:

// this example avoids adding <spark> to web.config
var settings = new SparkSettings();
 
// change settings, like
settings
    .SetDebug(true)
    .SetPageBaseType(typeof(MyBasePage));
 
var container = SparkPythonEngineStarter.CreateContainer(settings);
 
// change services, like
container.SetServiceBuilder<IViewActivatorFactory>(
    c => new MyViewActivatorFactory());
 
SparkPythonEngineStarter.RegisterViewEngine(
    ViewEngines.Engines, container);

Adding views

Spark views for IronPython look and act much like they would for csharp. They have a .spark extension and exist in the normal Views\<controllername>\<viewname>.spark path.

One of the most obvious differences would be the lack of a need to declare <viewdata/>. Other than that it's pretty darn straightforward. You can execute code inline with #statement and output information with ${expression} and all of the <content>, <macro>, support for underscored partials, imports, etc. is unchanged.

#import clr
<p>Methods available from the clr module</p>
<ul>
  <li each="x in dir(clr)">${x}</li>
</ul>
<p>Methods (and extension methods) available to the normal helpers</p>
<ul>
  <var underscores="'__'"/>
  <li each="x in dir(Html)" if="not x.startswith(underscores)">Html.${x}</li>
  <li each="x in dir(Ajax)" if="not x.startswith(underscores)">Ajax.${x}</li>
  <li each="x in dir(Url)" if="not x.startswith(underscores)">Url.${x}</li>
</ul>

So once you get past the subtle expression differences it's pretty straightforward. I relied very heavily on the Python language documentation figuring out the syntax to use. If you're comfortable with Python, or simply hate declaring your view data and mucking around with strong types and assembly references, using the DLR might be an attractive option.

<html>
  <head>
    <global Title="'Python View Language Sample'"/>
    <title>${Title}</title>
  </head>
  <body>
    <use:view/>
  </body>
</html>

#Title = "Products - " + Title
<p>There are ${len(products)} products available</p>
<ul>
  <li each="p in products">${Html.ActionLink(p.Name, "Show")}</li>
</ul>

Variable

Variable scope in Python is very different than in csharp because of the nature of the language itself. The entire view, and the entire master, are both single functions. All of the variables in those two functions are available to all of the views and partial files.

To share variables between view and master, use the global element, like <global title="'Default Title'"/>. Of course <content name="foo">...</content> and <use content="foo"/> can also be used to produce and consume material.

Other forms of variable declaration all end up meaning the same thing. <var x="5" hello="'world'"/>, <def x="5" hello="'world'"/>, and <set x="5" hello="'world'"/> are virtually interchangable - just remember to assign a variable before you use it.

Providing a fall-back value with <default x="1" hello="'(your name here)'"/> will work and is very convenient for assigning default values for optional arguments in partials.

ViewData

Viewdata does need to be declared because the contents of the dictionary will appear to be global symbols. Because they have a lower precedence than view class members you may need to use ViewData["name"] syntax to access information that collides with existing members.

public ActionResult Index()
{
  ViewData["foo"] = "bar";
  return View();
}

## no declaration needed
<p>Foo is ${foo}</p>

Looping and conditional

Iteration in Python doesn't require a type declaration on the looping variable. Otherwise it's very similar to Spark with csharp.

<for each="c in categories">
  <h3>${H(c.Name)}</h3>
  <ul>
    <li each="p in c.Products" class="alt?{pIndex%2}">${H(p.Name)}</li>
  </ul>
</for>

The conditional elements and attributes (test, if, elseif, else) in all their various forms work fine structurally once you take into account language differences. Python will use not, or, and and boolean keywords rather than punctuation operators. It will also infer the value of True for non-default values. So 0, False, and None (or null) all act as false. Which is why the condition ?{pIndex%2} above will give you even-odd striping by removing the alt css class as the result of 0 and 1 varies.

Inline code

The #statement<newline> format works especially well for adding Python code inline, but bear in mind how significant whitespace will be. The statement following the # character will be written at the correct indentation for the place it appears in scope, but there should only be whitespace between the # and the code if you're intentionally creating something scoped to a previous statement.

One use for that would be declaring a function.

<h3>Declaring a function</h3>
<ul>
  #import math
  ## Calculate the n-dimensional hypotenuse
  #def vectorlength(vector):
  #  dt = 0
  #  for d in vector:
  #    dt = dt + d * d
  #  return math.sqrt(dt)
  <li>3,4 : ${vectorlength([3,4])}</li>
  <li>12,13 : ${vectorlength([12,13])}</li>
  <li>0,5,3,2,0,6 : ${vectorlength([0,5,3,2,0,6])}</li>
</ul>

Note, since # is a comment in python ## can be used in Spark as a code-comment which won't appear in html output.

Invoking methods and helpers

All methods and properties of the base view class are available . If you're writing output explicitly there is a subtle problem with overloaded methods, like Output.Write(...), when the DLR determines which method to invoke. Writing a number for example will throw an exception because the several flavors of integer and floating point are ambiguous from the scripting point of view. Another method OutputWriteAdapter(object value) has been added to the scripting-specific spark view class, and that is the method ${expression} will invoke.

Some helper methods can be particularly tricky. It appears that native IronPython property-bearing reflect with put properties Keys and Values, so they can't be used for helper methods which take an anonymous type as a dictionary. The good news is you can typically call a different overload of the helper method by building the CLR dictionary classes.

<div>
  #import clr
  #clr.AddReference("System.Web.Routing")
  #from System.Web.Routing import RouteValueDictionary
  #routeData = RouteValueDictionary()
 
  #routeData["id"] = page.CurrentPage - 1
  <a href="${Url.Action(action, controller, routeData)}">&laquo; Previous</a>
 
  <for each="pageIndex in range(1, page.PageCount + 1)">
    #routeData["id"] = pageIndex
    <a href="${Url.Action(action, controller, routeData)}">${pageIndex}</a>
  </for>
 
  #routeData["id"] = page.CurrentPage + 1
  <a href="${Url.Action(action, controller, routeData)}">Next &raquo;</a>
</div>
 

Iron Ruby

About Iron Ruby

Iron Ruby is a dynamic language runtime (DLR) based implementation of Ruby. Spark's Ruby integration is based on slightly modified trunk code. Other good resources are Ruby documentation and Programming Ruby.

The build of IronRuby used for Spark has had the assemblies Microsoft.Scripting.* assemblies renamed NonStandard.Microsoft.Scripting.*. Those libraries appear to be works in progess and some irreconcilable differences existed in these shared assemblies in the IronPython and IronRuby sources which were available at the time the integration with Spark was done. As those projects evolve the need for two sets of assemblies should disappear.

The support for the DLR is very Asp.Net MVC centric. If there's an interest in expanded Castle MonoRail support, please speak up in the discussion group.

Creating a new project

A standard Asp.Net MVC web application is always a great starting place. First, add references to the following assemblies:

And the following from the IronRuby release which are available in the Spark bin\dependencies folder. If you have problems with the these files from RubyForge try the ones in from the Spark distribution. You have the "works on my machine" guarantee.

Yeah, I know. It's a lot of assemblies. You're going to love the next part though, adding the Spark view engine to an Asp.Net MVC web application has been simplified.

Add the following to your Global Application_Start method:

using Spark.Web.Mvc.Ruby;
...
protected void Application_Start(object sender, EventArgs e)
{
    SparkRubyEngineStarter.RegisterViewEngine();
}

This will create a view engine and add it to the engines collection.

The Spark engine starter also has several utility methods for more advanced initialization. You can, for example, provide a settings object from code. You can also use the ISparkServicesContainer to provide specific implementations of different services used by the Spark engine. For example:

// this example avoids adding <spark> to web.config
var settings = new SparkSettings();
 
// change settings, like
settings
    .SetDebug(true)
    .SetPageBaseType(typeof(MyBasePage));
 
var container = SparkRubyEngineStarter.CreateContainer(settings);
 
// change services, like
container.SetServiceBuilder<IViewActivatorFactory>(
    c => new MyViewActivatorFactory());
 
SparkRubyEngineStarter.RegisterViewEngine(
    ViewEngines.Engines, container);

Adding views

Spark views for IronRuby look and act much like they would for csharp. They have a .spark extension and exist in the normal Views\<controllername>\<viewname>.spark path.

The syntax of Ruby on Spark is a bit rhtml-like. Spark supports <%= ... %> and <% ...%> in the same way, and the dictionary of ViewData values are added as @name attributes. Of course, just like other Spark variations can also execute code inline with #statement and output information with ${expression} and all of the <content>, <macro>, support for underscored partials, imports, etc. is unchanged.

<html>
  <head>
    <global title="'Python View Language Sample'"/>
    <title>${@title}</title>
  </head>
  <body>
    <use:view/>
  </body>
</html>

#@title = "Products - " + @title
<ul>
  <li each="p in @products">${html.ActionLink(p.name, "Show")}</li>
</ul>

Invoking methods and helpers

All methods and properties of the base view class are available. Ruby naming convention may be in effect, so if a symbol like ViewData isn't resolved try using view_data.

Some helper methods can be particularly tricky. It appears that native IronRuby can't be used for helper methods which take an anonymous type as a dictionary. The good news is you can typically call those helper methods with a CLR dictionary class you can create. The following is a bit of simplified code from the samples\AspNetMvc\IronRubyViews project available in the download.

<div>
  ## make a clr route dictionary for url.action
  #require 'System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
  #routeData = System::Web::Routing::RouteValueDictionary.new
 
  ## monkey patch an id setter equivalent to {routeData["id"] = value}
  #def routeData.id=(value); set_Item('id',value); end
 
  #routeData.id = page.CurrentPage - 1
  <a href="${url.action(action, controller, routeData)}">&laquo; Previous</a>
 
  <for each="pageIndex in 1..page.PageCount">
    #routeData.id = pageIndex
    <a href="${url.action(action, controller, routeData)}">${pageIndex}</a>
  </for>
 
  #routeData.id = page.CurrentPage + 1
  <a href="${url.action(action, controller, routeData)}">Next &raquo;</a>
</div>
 

Reference

Spark Reference

Template elements, attributes, and configuration reference information

Elements


content

Captures output contained within the content element to a text spool or local variable.

with name=""

Usage: <content name="x">anyXml</content>

Spools all output in the content element into a named text writer. The named content can be placed as output later with a <use/> element, or can be accessed directly with the Content["x"] dictionary.

with var="" or def=""

Usage: <content var="x">anyXml</content>

Usage: <content def="x">anyXml</content>

Spools all output into a temporary text writer, then assigns the results into a new string local variable named by the var attribute value.

with set=""

Usage: <content set="x">anyXml</content>

Usage: <content set="x" add="before|after|replace">anyXml</content>

Spools all output into a temporary text writer, then assigns the results into an existing local variable named by the set attribute value.

If the content element also contains add="after" the capured content will be appended to the end of the variable's existing value. If it contains add="before" the content will be appended to the front of the existing variable value.


def

An alias for declaring a local variable, see <var/>.


default

Declares local variables if a symbol of a given name is not known to be in scope.

Usage: <default x="a" y="b"/>

var x = a;
var y = b;

If a symbol x or y is known to exist in scope as a local variable, or declared viewdata or global property, the line declaring the variable will not be generated.

with type=""

Usage: <default x="a" y="b" type="t"/>

t x = a;
t y = b;


else

Forms a part of an if/elseif/else conditional structure.

Usage: <else>anyXml</else>

else
{
  //anyXml-generated-code
}

Must follow an element that produces an if/elseif statement. Contains material that will be rendered if all preceding conditions are false.

with if=""

Usage: <else if="x">anyXml</else>

else if(x)
{
  //anyXml-generated-code
}

Must follow an element that produces an if/elseif statement. Contains material that will be rendered if all preceding conditions are false, and the expression x evaluates to true.

empty element

Usage: <test if="x">anyXml-1<else if="y"/>anyXml-2<else/>anyXml-3</test>

if(x)
{
  //anyXml-1-generated-code
}
else if(y)
{
  //anyXml-2-generated-code
}
else 
{
  //anyXml-3-generated-code
}

Must be contained within an <if> or <test> element. The material following an empty <else/> element is treated as if it was contained within that element. If there are multiple <else/> elements all but the last one must have an if="" attribute.


elseif

Forms a part of an if/elseif/else conditional structure.

Usage: <elseif condition="x">anyXml</else>

else if(x)
{
  //anyXml-generated-code
}

Must follow an element that produces an if/elseif statement. Contains material that will be rendered if all preceding conditions are false, and the expression x evaluates to true.


for

Iterates over a collection

Usage: <for each="var x in y">anyXml</for>

foreach(var x in y)
{
  //anyXml-generated-code
}

If the generated code appears to reference symbols named xIndex, xCount, xIsFirst, or xIsLast the minimum number of local variables needed will also be generated using a pattern similar to the following.

{
  int xCount = util_count_of(y);
  int xIndex = 0;
  bool xIsFirst = true;
  foreach(var x in y)
  {
    bool xIsLast = (xIndex == xCount - 1);
    {
      //anyXml-generated-code
    }
    xIsFirst = false;
    xIndex++;
  }
}

with *="" assignments

Usage: <for each="var x in y" a="b" c="d">anyXml</for>

foreach(var x in y)
{
  a=b;
  c=d;
  //anyXml-generated-code
}


global

Declares a property that's available to all templates while rendering.

Usage: <global x="a" y="b" type="t"/>

object _x = a;
public object x {get {return _x;} set {_x = value;}}
 
object _y = b;
public object y {get {return _y;} set {_y = value;}}

Although object seems like an inconvenient type, it works quite well when used in simple value assignments like <set x="42"/> and expression output like ${x}.

with type=""

Usage: <global x="a" y="b" type="t"/>

t _x = a;
public t x {get {return _x;} set {_x = value;}}
 
t _y = b;
public t y {get {return _y;} set {_y = value;}}


if

Forms a part of an if/elseif/else conditional structure.

Usage: <if condition="x">anyXml</if>

if(x)
{
  //anyXml-generated-code
}

Material in an if element will only render if the condition evaluates to true.

Alias: <test if="">


include

TODO: forgot to write up this one


macro

Declares a member helper function available to all templates while rendering.

Usage: <macro name="anyName" x="t1" y="t2">anyXml</macro>

public string anyName(t1 x, t2 y)
{
  //anyXml-generated-code
}

Macros may be used invoked as part of an output expression ${anyName(a,b)} or anyplace else code appears like <set z="anyName(a,b)"/> or # z=anyName(a,b);


render

Within a partial file, renders the material wrapped by the reference to that partial file.

Usage: anyXml2<render/>anyXml4

Given: anyXml1<partialFileName>anyXml3</partialFileName>anyXml5

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    //anyXml3-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code

When <render/> appears in a partial template it generates code for the contents of the element which referenced the partial. This can be used to have a partial file which "wraps" any sort of contained material in the parent template. This enabled partials to be used as content wrappers to produce rounded corners, ajax effects, around arbirtary content.

with segment="" (previously known as 'section')

Usage: anyXml2<render segment="anyName"/>anyXml4

Given: anyXml1<partialFileName><segment name="anyName">anyXml3</segment></partialFileName>anyXml5

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    //anyXml3-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code

When <render segment=""/> appears in a partial template it renders the contents of the <segment name=""> contained in the referencing <render partial=""> template. This enables partials to render several distinct micro-templates, for example a repeater partial could have top/bottom/even/odd named segments.

See also: <segment/>

with *="" assignments

Usage: anyXml2<render x="a" y="b"/>anyXml4

Given: anyXml1<partialFileName>anyXml3</partialFileName>anyXml5

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    var x = a;
    var y = b;
    //anyXml3-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code

Partial files may declare local variables when rendering passed in content. They are available to the scope of the wrapped content or named segment in the outer template file.

with fallback content

Usage: anyXml2<render segment="anyName" x="a" y="b">anyXml-fallback</render>anyXml4

Given: anyXml1<partialFileName/>anyXml5

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    var x = a;
    var y = b;
    //anyXml-fallback-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code

When the reference to a partial file is empty, or the named segment does not exist, the contents of the <render> element in the partial file will generate the rendering code by default.

Given: anyXml1<partialFileName>anyXml3</partialFileName>anyXml5

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    var x = a;
    var y = b;
    //anyXml3-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code

When the reference to a partial file has contents, the contents of the <render> element in the partial file is unused.


render partial=""

Includes the contents of a partial file for rendering at the point where it appears.

Usage: <render partial="_partialFileName"/>

Alias: <partialFileName/>

{
  //_partialFileName.spark-generated-code
}

Partial files may have any name, but usually start with _ by convention. The name without the underscore may be used as a short version of the <render/> element.

with *="" assignments

Usage: <render partial="_partialFileName" x="a" y="b"/>

{
  var x = a;
  var y = b;
  //_partialFileName.spark-generated-code
}

A partial file reference may declare local variables. They are available to the scope of the wrapped content or named segment.


segment (previously known as 'section')

Declares a segment of material passed in to a partial file reference.

Usage: anyXml1<partialFileName><segment name="anyName">anyXml3</segment></partialFileName>anyXml5

Given: anyXml2<render segment="anyName"/>anyXml4

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    //anyXml3-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code

When <render segment=""/> appears in a partial template it renders the contents of the <segment name=""> contained in the referencing <render partial=""> template. This enables partials to render several distinct micro-templates, for example a repeater partial could have top/bottom/even/odd named segments.

with *="" assignments

Usage: anyXml1<partialFileName><segment name="anyName" x="a" y="b">anyXml3</segment></partialFileName>anyXml5

Given: anyXml2<render segment="anyName"/>anyXml4

//anyXml1-generated-code
{
  //anyXml2-generated-code
  {
    var x = a;
    var y = b;
    //anyXml3-generated-code
  }
  //anyXml4-generated-code
}
//anyXml5-generated-code


set

Assigns an expression to a variable.

Usage:

x = a;
y = b;

This is arguably the least interesting element in the entire Spark library. But if it wasn't there you'd probably have to escape assignments into code fragments like #x = a;. So there you go.


test

Forms a part of an if/elseif/else conditional structure.

Usage: <test if="x">anyXml</test>

if(x)
{
  //anyXml-generated-code
}

Material in a test element will only render if the condition evaluates to true.

Alias: <if condition="">


use assembly=""

Ensures a partially or fully qualified assembly name is include when the Spark assembly is compiled.

Usage: <use assembly="system.something.something"/>

Usage: <use assembly="system.something.something, etc=20394824, etc=239483432"/>


use content=""

Renders whatever has been captured in a named content spool.

Usage: <use content="x"/>

if (Contents.ContainsKey("x"))
{
  Output.Write(Contents["x"]);
}

The use of content spooling is actually very efficient. It uses pooled pages of string references to replay the original output, which doesn't increase memory pressure and avoids string copying and concatination costs.

There is a built-in content spool named "view".

with fallback content

Usage: <use content="x">anyXml-fallback</use>

if (Contents.ContainsKey("x"))
{
  Output.Write(Contents["x"]);
}
else
{
  // anyXml-fallback-generated-code
}

If the named content segment has never been written into the fallback output is generated. This can be used for default title contents, for example <title><use content="title">My Default Title</use></content>, or for wrapping any html in the layout you may want to entirely replace in some views.


use file=""

An alias for <render partial=""/>.

Usage: <use file="_partialFileName"/>

All behavior is identical to render partial.


use import=""

Includes all of the elements which have a global effect declared in a template.

Usage: <use import="fileName"/>

This can be used to organize elements like <viewdata/>, <global/>, <macro/>, <use import=""/>, <use assembly=""/>, <use namespace=""/> into importable template files for greater manageability.

The well-known file named _global.spark in the current template's directory, and in the Shared directory, is automatically imported.


use master=""

Forces the layout around the current template file to become the named file.

Usage: <use master="fileName"/>

This will override the master which would normally have been used. Multi-level rendering can be accomplished by chaining templates with use master="" elements.

This element has no effect when it's in a partial file or import file.


use namespace=""

Uses a dotnet namespace in the generated code.

Usage: <use namespace="any.name.space"/>

using any.name.space;

Uses a namespace. Frequently placed in an import file like _global.spark.


var

Declares a local variables and it's initialization expression.

Usage: <var x="a" y="b"/>

var x = a;
var y = b;

with type=""

Usage: <var x="a" y="b" type="t"/>

t x = a;
t y = b;


viewdata

Declares an accessor property for a piece of view data.

Usage: <viewdata x="t1" y="t2" />

public t1 x { get { return (t1)ViewData.Eval("x"); } }
public t2 y { get { return (t2)ViewData.Eval("y"); } }

with default=""

Usage: <viewdata x="t1" y="t2" default="d"/>

public t1 x { get { return (t1)(ViewData.Eval("x") ?? d); } }
public t2 y { get { return (t2)(ViewData.Eval("y") ?? d); } }

Adds a fallback value for the property in cases where the actual view data is missing or null.

with property name

Usage: <viewdata x="t1 anyName1" y="t2 anyName2" />

public t1 anyName1 { get { return (t1)ViewData.Eval("x"); } }
public t2 anyName2 { get { return (t2)ViewData.Eval("y"); } }

A name following the type will determine the name of the property which is added. This enables you to declare viewdata accessors which contain dashes or dots, which are not allowed in property names.


viewdata model=""

Provides a generic parameter to strongly type the Model of the view's base class.

Usage: <viewdata model="t1" />

public class generated_view_name : base_view_name<t1>
{
  public t1 Model { get { return ViewData.Model; } }
}

The base view class is strongly typed, and a typed property named Model is added to the generated view.

with property name

Usage: <viewdata model="t1 anyName" />

public class generated_view_name : base_view_name<t1>
{
  public t1 Model { get { return ViewData.Model; } }
  public t1 anyName { get { return ViewData.Model; } }
}

A name following the type causes an additional property of that name to be added as an alias for the Model property.


See also: source code

The above is based on code in the following files.

ChunkBuilderVisitor.cs

GeneratedCodeVisitor.cs

Bindings

About bindings

A binding is a small rule that matches an element in a spark file and provides a code fragment that takes its place in the generated class.

Adding bindings to a project

By default bindings are loaded from a ~/Views/Bindings.xml file if it is present in your project. There are two main forms of bindings,

<bindings>
  <element name="TheElementName">TheReplacementCode</element>
</bindings>

<bindings>
  <element name="TheElementName">
    <start>StartElementReplacementCode</start>
    <end>EndElementReplacementCode</end>
  </element>
</bindings>

It is loaded through the IViewFolder abstraction so it does not need to be a physical file if that path is provided by another source.

You may also replace the default IBindingProvider service on SparkViewEngine instance if you want to entirely replace how binding definitions are located and parsed.

Simple code replacement of an element

By default a binding will match an element by name, and the replacement code will be used as an output expression.

<bindings>
  <element name="Greetings">"Hello, " + Model.CurrentUser.Name + "!"</element>
</bindings>

<!-- using the binding -->
<p><Greetings/></p>
<!-- is the same as -->
<p>${"Hello, " + Model.CurrentUser.Name + "!"}</p>

Using attributes as parameters

The replacement code may contain small terms that are designed to resemble xpath selectors. If they are present it has two effects: the rule will only match elements in the spark template which have those attributes, and the value of the attribute will replace the term in the generated code.

<bindings>
  <element name="Greetings">"Hello, " + @username + "!"</element>
</bindings>

<!-- using the binding -->
<p><Greetings username="Product.Name" /></p>
<!-- is the same as -->
<p>${"Hello, " + Product.Name + "!"}</p>

Possible matches for bound elements are evaluated in order of appearance from the binding provider. The first one that satisfies all requirements is used, so to overload an element put the most specific bindings first.

<bindings>
  <element name="Greetings">"Hello, " + @username + "!"</element>
  <element name="Greetings">"Hello, " + Model.CurrentUser.Name + "!"</element>
</bindings>

<!-- using the binding -->
<p><Greetings/></p>
<p><Greetings username="Product.Name" /></p>
<!-- is the same as -->
<p>${"Hello, " + Model.CurrentUser.Name + "!"}</p>
<p>${"Hello, " + Product.Name + "!"}</p>

Treating attribute values as text by default

There are times when an attribute containing code is given a string value, and the nested quotes seems counter-intuitive. In the following example you wouldn't assume the quotes around the action and text are needed.

<bindings>
  <element name="ActionLink">Html.ActionLink(@text, @action)</element>
</bindings>

<p>Please <ActionLink action="'Register'" text="'register'"/> to continue.</p>

To make the quotes implied, wrap the attribute in the reference with single- or double-quotes. The attribute values in the spark templates will be treated as text, and may contain ${expr} to mix anything dynamic into the generated code.

<bindings>
  <element name="ActionLink">Html.ActionLink('@text', '@action')</element>
</bindings>

<p>Please <ActionLink action="Register" text="register before ${DateTime.Now.AddSeconds(3)}"/> to continue.</p>

Binding elements that are already meaningful

If an element name has bindings, but it does not match any of the rules because of missing attributes, then it will be output as if it was plain markup like any other element in the temples. That can be useful to give special meaning to existing elements, like <a>, if it contains some meaningful attributes you define, like action="...".

<bindings>
  <element name="a">Html.ActionLink('@text', '@action')</element>
</bindings>

<p>Please <a action="Register" text="register before ${DateTime.Now.AddSeconds(3)}"/> to continue.</p>

Using child text as a value

If you want use a binding element around some child content, and that child content must be passed to a macro or html helper as an argument, you can refer to it with a 'child::*' term.

<bindings>
  <element name="a">Html.ActionLink('child::*', '@action')</element>
</bindings>

<!-- normal usage of a link, followed by an action="" attribute to use the html helper  -->
<p>
  Either <a href="~/">return to the home page</a> 
  or <a action="Register">register before ${DateTime.Now.AddSeconds(3)}</a>
  to continue.
</p>

Initializing dictionary with wildcard attributes

Html helpers often take a dictionary of name/value pairs to do things like add route values, or additional html attributes, to the output they produce. An attribute wildcard syntax {{'@*'}} may be used to expand any unused attributes into a new dictionary.

<bindings>
  <element name="a">Html.ActionLink('child::*', '@action', '@controller', 
    new Dictionary&lt;string,object&gt;{{'@*'}})</element>
</bindings>

<p>Please <a action="Register" controller="Account" title="This is the hover text">register</a> to continue.</p>

Because @action and @controller are in the replacement code they will not appear in the dictionary. The attribute value may also contain code in ${expr} syntax.

Initializing several dictionaries with prefixes

There are cases where a helper may take several dictionaries. One example would be route values in addition to html attributes. In that case a prefix may be used in the wildcard attribute reference.

<bindings>
  <element name="a">Html.ActionLink('child::*', '@action', 
    new RouteValueDictionary{{'@route-*'}}, 
    new Dictionary&lt;string,object&gt;{{"@*"}})
  </element> 
</bindings>

<ul>
  <li each="var product in Products">
    Id ${product.Id}: 
    <a action="show" route-id="${product.Id}" title="${product.Details}">${product.Name}</a>
  </li>
</ul>

Initializing objects and anonymous objects

If a wildcard element is not surrounded with a double-curley brace, then the code generated will be suitable for object initialization.

<bindings>
  <!-- element Foo will call function Foo() -->
  <element name="Foo">Foo(new Thing{'@*'})</element>
</bindings>

<!-- macro puts Foo() in scope. assumes class Thing exists. -->
<macro name="Foo" theThing="Thing">
  <span>Thing ${theThing.Id}: #{theThing.Name}</span>
</macro>
 
<p>  
  <Foo Id="${34}" Name="Frank"/>
  <!-- is the same as -->
  ${Foo(new Thing {Id = 34, Name="Frank" })}
</p>
</bindings>

No return value

Some helpers that write directly to the response and have a void return value. If you put a hash at the front of a binding it will simply generate the code as a statement and omit the output.

<bindings>
  <element name="Partial">#Html.RenderPartial("@name", new ViewDataDictionary{{"@*"}});</element>
</bindings>

  <Partial name="ShowThis" Foo="My Caption" Bar="${Model.Product}" />

Wrapping child text with two code replacements

Another way to use a binding as a element with child contents is by providing two replacement code fragments. In this case the child content is output normally - it is not marshalled into a parameter - the start and end code is simply inserted at the point where the start and end element appeared.

<bindings>
  <element name="Form">
    <start># using (Html.BeginForm("@action", "@controller", new RouteValueDictionary{{"@route-*"}}, FormMethod.@method, new Dictionary&lt;string,object&gt;{{"@*"}})) {</start>
    <end># }</end>
  </element>
</bindings>

<div>
  <Form action="Create" controller="Product">
    <!-- input elements and whatnot -->
  </Form>
</div>