How many ways do you need for a file to include another one?
geek, opensource, programming, spark September 11th, 2008Turns out the answer is four! No wait… Five? Definitely less than six at least.
This is talking specifically about different ways one Spark files can pull in, render, or otherwise import another one. Of course the goal is not to create an unmanageable mess of individual files, rather to organize your files in a way that that the contents are more manageable and the html from one template to another isn’t unnecessarily redundant.
So let’s walk through the situations! Starting with the obvious ones.
Master/Layout File
These .spark files go in the Views/Layouts or Views/Shared folder. Monorail has a [layout] attribute which specifies the name of the layout file that should be used. Asp.Net Mvc has a string masterName argument in the call to View(). Because there isn’t an attribute on the controller to provide a default layout it will use the convention of default the layout with the name of the controller, like Account.spark for the AccountController, or failing that defaulting to a layout named Application.spark, if either of those files exist.
Inside the master/layout file you will have an element that says <use content=”view”/> or simply <use:view/> This isn’t really including the view file, since layouts are rendered in a multi-pass mechanism. First the view is rendered to a string builder, then the master is rendered with that string builder assigned to “view”. The reason for that is to make it possible for the view to alter the title and generate content into head, left and right areas, etc without concern about the physical location in the output affecting the order of execution.
Spark Partial files
A partial file is anything that starts with an underscore (_) and has a .spark extension. At any point in your template you may render the contents of a partial file with the element <render partial=”_MyPartial”/> or with the partial name as an element <MyPartial/>.
Interestingly enough, when you reference a partial file in this way the ViewLoader doesn’t immediately drop what it’s doing, jump over to parse the partial file to generate it’s code, and jump back to continue processing where it left off. Rather the current template gets a RenderPartialChunk placeholder at that location, and the ViewLoader adds the target file to it’s “todo” list if it hasn’t been loaded already. The loader will continue to parse and chunk out files in it’s “todo” list (never the same file more than once of course) until all of the file references have been chunked.
Then when it goes to generate code from the template’s chunks when it hits a RenderPartialChunk it simply looks up that resource, and drills down into it’s chunks to generate that code, then continues to go down the template where it left off.
Why bother knowing this? I dunno. It’s kind of interesting. What’s the net effect? In a nutshell your view will be one long function. If a partial is rendered three times, the code which outputs that partial will be present three different times at the appropriate locations. That’s also the way a partial file has access to the correct local variables from the template for the correct location each time it is utilized.
If a partial renders sections from the parent template it’s the same story. The point where that occurs is when the code is being generated. Say a template is using a partial that wraps some of it’s content with a rounded box. The chunks being iterated are from the template, then from the partial, then back from the original template inside the partial, then back from the remainder of the partial, then finishing with the remainder of the template. The code generator zig-zags a bit when it’s iterating chunks, but once the csharp code is generated it’s simple top-down in-order output write statements.
Asp.Net MVC partial file
As of Preview 5 Asp.Net MVC has an excellent implementation of rendering partial files which is totally different and also very powerful and convenient.
<% Html.RenderPartial(”_MyPartial”) %>
This extrinsic partial mechanism has nothing to do with the intrinsic partial files described above. This line of code is simply compiled right in to your view, and when Spark (or any view engine) calls RenderPartial the Asp.Net MVC framework will assume the responsibility of turning to all of the registered view engines and ask them, in order, to produce an IView instance of the requested name.
So these partials have a far greater separation of execution context. They can’t share global or local variables, or produce and consume named content, but on the plus side there’s zero chance of variable naming collisions. If the partial works, then it works, and any variables and cruft declared in any views won’t change that.
Plus it enables you to cross engine technology boundaries. In the Spark download there’s a dual-engine sample which has an aspx view template utilizing an ascx partial and a spark partial, right next to a spark view template which uses the same ascx and spark partial files.
Import files
There are a number of things you can put into a spark file which declare macros, add namespaces, declare globals and viewdata properties, etc. All of these things can be put into a spark file along with the content being rendered, but for clarity and organization it’s often better to put them into a seperate file and import them.
If you have several views and a few layouts for example, you might have some macros you would want to use from everyplace. Putting <use import=”CommonMacros”/> in several places is a much better way of handling that than duplicating the <macro> elements in each of the layouts.
Or if you have some shared functions used by a handful of partials, but some pages don’t use any of those partial files. In that case you could put those macros into a specific file, and import it from each of the partials, at which point any page which uses one or more of those partials will also generate functions from that file a single time.
As a final tribute to “convention over configuration” if you create a _global.spark file in a controller’s folder it will be imported for all templates coming from that location. And if you create a _global.spark file in the Shared folder it will be imported with every view class that spark generates.
Include files
This is the most direct and possibly the easiest to understand mechanism. It’s based on the XInclude specification from a request by Brian J. Cardiff.
With an <include href=”relative/filename.spark”/> element the contents of another file will be pulled in directly at that location as if it was there the whole time. So this would be a great way to take a very large resource and split it apart into manageable parts. The parse attribute and fallback element are also supported.
The parse=”text” attribute has a particularly interesting effect - the target file is always brought in as a single text node with xml characters encoded if needed. So you could bring in .txt files with release notes this way, or legal disclaimers, or css file bodies, or even spark files which you want to have rendered as their escaped source rather than parsed and converted to code.

Recent Comments