Spark in practice

I have to say I’m really happy with how the view engine turned out. It’s being dog fooded on some prototyping at the moment and there are lots of things that I really appreciate as it’s going along.

Consider the following partial file that renders a tab strip followed by a number of pages. Each page has a number of items…

_MostPopular.spark

<div id="MostPopular">

  <content name="head">
    <link rel="stylesheet" type="text/css"
          href="${SiteRoot}/content/css/home-mostpopular.css"/>
    <script type="text/javascript"
            src="${SiteRoot}/content/js/home-mostpopular.js"></script>
  </content>

  <div class='header'>Most Popular</div>

  <ul class='tabs'>
    <var i="0">
      <li each="var page in mostPopularData.Pages"
          id='MostPopularTab${++i}'
          class='tab ${i == 1 ? "selected" : ""}'>
        <a href='#'>${page.Title}</a>
      </li>
    </var>
  </ul>

  <div class='pages'>
    <var i="0">
      <ul each="var page in mostPopularData.Pages"
          id='MostPopularTab${++i}Page'
          class='page ${i == 1 ? "selected" : ""}'>
        <li each="var item in page.Items.Take(5)">
          <a href="${item.AbsoluteUrl}">${H(item.Headline)}</a>
        </li>
      </ul>
    </var>
  </div>

</div>

So first off - this is how visual studio xml editor auto-indented everything so the loops and conditionals match up really nicely. I also really like how the ability to capture content into a named section turned out. The <content name="head"> will not be output - instead it will be reproduced verbatim when the <use content="head"/> appears in the head element in the layout template.

There’s also how you can use the each="var thing in things" attribute to wrap an element in a for loop. The page content is a ul/li with an each on the ul and an each on the li. That’s all the code you need to render nested collection. Something else that’s nice is when you have <use namespace="System.Linq"/> in the view or master file you can use LINQ collection extension methods like the .Take(5) you see above. That limits the list to be at most five items.

Some feedback from was around the use of <var i="0"> and ++i to have the item index available. It works but it’s too heavy for such a common use case. The thinking right now is to have the code generator add an int thingIndex for free when you’re iterating var thing in… So the example should be simpler soon - using pageIndex and itemIndex instead of ++i code.

Here’s an excerpt from an index.spark file that’s putting this partial file widget and others in a right rail…


  <content name="RightRail">
    <ShowBlock key='"MarketingPromo"'/>

    <div class="contentBlock">
      <AdZone width="300" height="250"/>
    </div>

    <viewdata mostPopularData="SampleProject.Models.MostPopularData"/>
    <MostPopular/>
  </content>

It’s the <MostPopular/> which is shorthand for <use file="_MostPopular.spark"/>. The viewdata element right before it has the effect of adding a typed property-get to the generated class.

SampleProject.Models.MostPopularData mostPopularData
{ get { return (SampleProject.Models.MostPopularData)ViewData["mostPopularData"]; } }

For monorail purposes the ViewData is a string/object collection which aggregates the same sources the nVelocity context normally has available. So in this case it’s reaching the PropertyBag["mostPopularData"] object.

And finally here’s an excerpt from the default.spark master file


    <div id="PrimaryContentContainer">
      <RootNavigation/>

      <div id="PageContent">
        <use content="view"/>
      </div>

      <div id="RightRail">
        <use content="RightRail"/>
      </div>

      <global errorcount="0" type="int"/>
      <div class='errors' if='errorcount!=0'>
        <p>$errorcount; errors</p>
        <use content='errors'/>
      </div>

Also at the bottom there you can see where an errorcount int was added to the page. So anywhere in the rendering stack you can ++errorcount and <content name=”errors”><p>add errors to the red-bordered window like this this</p></content>

And any time you see a wierd element it’s probably rendering a partial file. Like the following placeholder.

_AdZone.spark

<div class="stubAdZone" style="height:${height}px; width:${width}px;">
  ad
</div>

Note the other trick here… When a partial is referenced and it has attributes like height="250" it’ll put new local variables in scope. That’s where ${height} and ${width} are coming from when they appear in this partial file. If you didn’t have those attributes in the <AdZone/> tag you’d get a view compile-time error…. Yep, you’ll get something like “error CS0103: The name ‘width’ does not exist in the current context”.

Tags: , , ,

Leave a Reply