This will be another post about the MVC view engine spark, by the title of this post and my last post you can safely assume that I really like this view engine.

Since my last post about spark there has been some big updates to the view engine and a bunch of new features.

Conditional attribute

<input type="checkbox" name="chkhello" checked="?{isHelloChecked}"></input>
The conditional ?{boolean} syntax if used as the only thing in an attribute will remove the attribute from the rendering (if the boolean expression is false). If there is text in front of the question mark then only that text will be excluded, for example:
<ul>
  <li each="var product in Products" class="first?{productIsFirst} last?{productIsLast}">
    ${H(product.Name)}
  </li>
</ul>

The above will output:

<ul>
  <li class="first">Alpha</li>
  <li>Beta</li>
  <li>Gamma</li>
  <li class=" last">Delta</li>
</ul>

More loop autovariables

In loops the view engine will automatically provide some loop variables, the name is always the loop variable name plus the suffix: "Index", "IsFirst", "IsLast", "Count". It will only generate loop variables that you use so there is no performance hit if you don't use any of them. IsLast and Count could potentially have a small affect on performance (for example for IEnumerable LINQ lists).

Macros

This is a great little feature to reduce duplication in your views:
<macro name="PrevNextLink" cs="Changeset" className="string">
    <if condition="cs != null">    
        <a href="${Url.RouteUrl('ChangesetDetail', new {cs=cs.Revision})}" class="${className}" >
            ${cs.Message.TrimWithElipsis(50)}
        </a>        
    </if>    
</macro>    

<div class="changeset-nav-links">
    ${PrevNextLink(ViewData.Model.Previous, "prev")}
    ${PrevNextLink(ViewData.Model.Next, "next")}    
</div>

A macro translates to a function that returns a string. This function can then be used anywhere in your view. Great for reusing some small part of a view that you do not feel needs to be a separate view file (partial).

Partial views

Spark has great support for partial view files. You can render content from a partial by using implicit or explicit syntax.

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

This will look for a view file named mypartial.spark

Implicit:
<menuTabs tabs="ViewData.Model.Tabs" />

This will look for a partial file named _menuTabs.spark, first in the current view folder, then in the shared view folder. Partials can access all scoped variables from where it is used, optionally you can set locally scoped variables for the partial by setting attributes (for example the tabs attribute in the above example).

Partial views and inner content

There is a new feature that allows you to send the inner content to a partial view.

Example partial file _boxit.spark:

<div class="boxit">
  <div class="boxit-header">
    ${header}
  </div>
  <div class="boxit-content">                        
    <render/>
  </div>
</div>

This can than be used like this:

<p>
  Something.. 
</p>

<boxit header="Cool box">
  <p>
    Some inner content for the box, can be anything, 
    you can use a another partial in here for example.
  </p>
</boxit>

<p>
  More something.. 
</p>

The idea for this actually came from a college of mine, I thought it was good idea so I submitted it to the spark mailing list and Louis DeJardin did a great job implementing it. He is even working on expanding this to add support for named sections within partials, similar to what MonoRail view components has.

Precompiling

You can now precompile your views, for example you can create a unit test that validates that your views compile successfully:

[Fact]
public void Can_Compile_HistoryViews()
{
    var batch = new SparkBatchDescriptor();

    batch.For<HistoryController>()
        .Layout("application")
        .Include("History")
        .Include("Browse")
        .Include("Changeset");
    
    var assembly = factory.Precompile(batch);

    Assert.NotNull(assembly);
    Assert.Equal(3, assembly.GetTypes().Length);
}

Configuration settings

You can now specify referenced assemblies and included namespaces for all your views, this can be done from web.config or from code. This is great as your views will normally use the same namespaces, for example a domain model / presentation model namespace and a namespace with helpers. You can also specify a base class for the generated view class.

Alternative syntax for named elements

Many features in spark use a name attribute on elements. There is now an alternative syntax, instead of <foo name="bar">  you can write <foo:bar>.

<content name="sidebar">
  xxx
</content>

<use content="sidebar"/>

Alternative:

<content:sidebar>
  xxx
</content:sidebar>

<use:sidebar/>

Summary

There are other new stuff, for example you can instantiate your generated view classes using an IoC container. The trunk version of Spark is already updated to use the recently released MVC Preview 5. For more info checkout the official site dev.dejardin.org, it contains documentation (most of the new stuff seems to be included in the docs), as well as a recent build of the trunk.

1 comments:

Anders said...

Very cool, I also like the spark syntax, not as crazy as Haml but still very nice.