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.