In the 2nd part of my serious effort to prove that ScottGu is lying when it comes to the new ASP.NET MVC framework, I decided to tackle another of his claims:
The ASP.NET MVC Framework supports the ability to use any templating engine to help with generating UI (including existing templating engines like NVelocity, Brail - as well as new ones you write yourself). -- ScottGu
So I decided to use NVelocity, since it's the one Scott mentioned first, and it's the one of the ones the Castle MonoRail folks use, so it can't be all bad.
Getting Started - Setup
I downloaded the latest NVelocity release, and added references to my ongoing scratchpad MVC project in VS2008 and I created a new IViewFactory implementation. Here's the skeleton/shell:
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Commons.Collections;
using NVelocity;
using NVelocity.App;
using NVelocity.Context;
namespace ASPNETMVCCookbook
{
public class NVelocityViewFactory : IViewFactory
{
public IView CreateView(
ControllerContext ctlCtx,
string viewName,
string masterName,
object viewData)
{
throw new NotImplementedException();
}
}
}
Creating the IView Implementation
Next, I had to create an implementation of IView also, in order to handle the rendering part. There were a couple ways I could've done this, but since I was just playing around, I did it the easiest way I could:
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Commons.Collections;
using NVelocity;
using NVelocity.App;
using NVelocity.Context;
namespace ASPNETMVCCookbook
{
public class NVelocityView : IView
{
private object _viewData;
private Template _template;
private VelocityContext _ctx;
public NVelocityView(
object viewData,
Template template,
VelocityContext velocityContext)
{
_viewData = viewData;
_template = template;
_ctx = velocityContext;
}
public void RenderView(ControllerContext ctlCtx)
{
_template.Merge(
_ctx,
ctlCtx.HttpContext.Response.Output);
}
}
}
Adding the VelocityEngine and Locating the View Template
Next, I grabbed the basic sample code for hosting the VelocityEngine from the Castle Project's NVelocity documentation page. Now our CreateView method has this body:
public IView CreateView(
ControllerContext ctlCtx,
string viewName,
string masterName,
object viewData)
{
string controllerName = (string)ctlCtx.RouteData.Values["controller"];
VelocityEngine engine = new VelocityEngine();
ExtendedProperties props = new ExtendedProperties();
engine.Init(props);
string templatePath = String.Format(
"/Views/{0}/{1}.vm",
controllerName,
viewName);
Template t = engine.GetTemplate(templatePath);
VelocityContext ctx = new VelocityContext();
ctx.Put("viewdata", viewData);
return new NVelocityView(viewData, t, ctx);
}
Creating Some Views
I already had some views created when I was using the WebForm view factory (ASPX files). I had List.aspx and Edit.aspx. I decided to turn these into Velocity templates. This is where I fell out of my comfort zone and had to hack and slash a little. I learned that NVelocity appears to require you to have the files as embedded resources in your project. I tried seventeen different ways to get them to load 'em from the file system, but couldn't get it to work. In the end, I ended up embedding them and it worked like a charm. I had to do some extra work to figure out the paths for there the views would be. My view locating logic isn't very robust, but most of the good logic that the MVC's WebFormViewFactory uses for figuring out where the views are are locked up in other places in the MVC framework and I can't get to them as an implementer so I'll have to reinvent some of it for now.
For my List.vm (.vm is the file extension Velocity templates normally use, but that's not required, it could be .html), I used the following Velocity Templating syntax in the HTML:
#foreach( $recipe in $viewdata )
<li><a href="Edit/$recipe.ID">$recipe.Name</a></li>
#end
$viewdata, in this case, is an IEnumerable<Recipe> returned from the RecipeRepository.FindAll() method called by the RecipeController and passed to the view.
Running it
Once this was all done, I compiled and fired up the ASP.NET WebDev server and hit my localhost and went to /Recipes/List and viola, it worked! Here's the raw HTML that NVelocity spewed out:
<li><a href="Edit/1">Recipe 1</a></li>
<li><a href="Edit/2">Recipe 2</a></li>
<li><a href="Edit/3">Recipe 3</a></li>
<li><a href="Edit/4">Recipe 4</a></li>
<li><a href="Edit/5">Recipe 5</a></li>
Which is exactly what I wanted in this case.
Clicking on one of the links takes me to the Edit view which is also NVelocity and it rendered perfectly too.
Foiled Again
Once again, Scott manages to evade me and continue making his outrageous claims. Until next time...

Technorati Tags:
ASP.NET,
MVC,
NVelocity