How to use Razor in Umbraco
This article is about the upcoming version of Umbraco (4.6.2), that uses normal .cshtml files instead of the .razor file extension. Examples will also be presented in HTML 5 format, so do remember that the tags are part of HTML 5, not Razor.
Disclaimer: The Razor implementation is still very new and changing rapidly, some of this information might already be outdated.
When using the Razor view engine it's important to remember that you are just writing HTML and, when you need some data, you switch context by starting with the "@" sign and adding some C# (yes, you can also make .vbhtml files and start writing VB, if you must):
<article>
<header><h2>@Model.Name</h2></header>
<img src="@Model.ImageUpload" alt="@Model.Name" />
@(Html.Raw(Model.BodyText))
</article>
In umbraco's case, the "Current" object holds the same data as the "currentPage" variable does in XSLT. All of the properties that are on the current document type are available to you, although you only get intellisense with the default properties (Id, Name, CreateDate, etc.).
As you can see, there's an interesting mix of html and @ signs. Not much to explain here except for the Html.Raw call, this is the same as saying disable-output-escaping="true" in XSLT. If you don't do this, all of the text will be encoded as HTML entities.
Now we have pretty decent looking page, let's make it a little more complex. Say I have a few news articles somewhere in my site, the titles of which I would like to display in a sidebar at all times. I could go and look for those nodes by looking at the Model.Parent and up and up until I was at level 1 and then find the child node with the "Article" type, but that's quite a bit of work to do every time I need to do something similar*.
It was much easier just to use Linq 2 Umbraco and query Umbraco's cached content (I'm open to suggestions if there's a better way).
* Although UQL will make this a bit easier
So now I get a Razor template that looks like this:
@using System;
@using System.Linq;
@using umbraco;
@{
var context = new MyNamespace.MyModelDataContext();
var catArticles = context.Articles.OrderByDescending(x => x.CreateDate).ToList();
}
<article>
<header><h2>@Model.Name</h2></header>
<img src="@Model.ImageUpload" alt="@Model.Name" />
@(Html.Raw(Model.BodyText))
</article>
<aside>
<ul>
@foreach (var article in catArticles)
{
<li>
<a href="@library.NiceUrl(article.Id)">@article.NodeName</a>
<li>
}
</ul>
</aside>
Still pretty easy, now it's time for a real challenge. I have an image upload field and an image cropper on the article pages. The image cropper stores bits of XML for the values of the different crops. So just doing a @article.ImageCrop won't work. Here's where helpers come in.
Helpers can be created inline, but it's better when you just create a new .cshtml file in your App_Code and refer to that helper. It becomes reusable and actually helps you write better modular code. In this case the helper will return a value so it's called a function.
Careful here: if you add a .cshtml file to the App_Code folder through the Visual Studio interface, it will also add a key in the web.config:
<add key="webpages:Enabled" value="true" />
This wil result in a "yellow screen of death", complaining that the UmbracoMembershipProvider is configured incorrectly: "Parser Error Message: This method cannot be called during the application's pre-start initialization stage."
So I've created a tiny little XML parser (untested in the real world, be sure to rename from .txt to .cshtml) and will use that to get the cropped image out:
<aside>
<ul>
@foreach (var article in catArticles)
{
<li>
<a href="@library.NiceUrl(article.Id)">
<img src="@XmlFunctions.GetAttributeValue(article.ImageCrop), "/crops/crop[@name = 'Small']", "url")" alt="@article.NodeName" />
</a>
<li>
}
</ul>
</aside>
Note that the namespace of the helper is the filename I've created in the App_Code folder, so XmlFunctions.cshtml translates to the XmlFunctions namespace in the Razor template.
And that concludes the first part of a few Razor posts.
For more info, be sure to check out these links:
- http://www.aaron-powell.com/umbraco-4-and-razor
- http://www.asp.net/webmatrix/tutorials/2-introduction-to-asp-net-web-programming-using-the-razor-syntax
- http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx
- http://joeriks.wordpress.com/2011/01/12/razor-inline-helpers-in-umbraco-juno-full-sitemap-sample/
- http://joeriks.wordpress.com/2011/01/04/playing-with-razor-in-umbraco-an-old-skool-contact-form/
- http://haacked.com/archive/2011/01/06/razor-syntax-quick-reference.aspx
8 comments on this article
Pinal Bhatt | February 16 2011 17:23
Thanks for the update. But i have few queries:
1. what is ETA for Umbraco 4.6.2?
2. Will I have to re-write my razor scripts created in 4.6.1 using "Model" and .razor files?
Thanks & Regards
Pinal Bhatt
Pinal Bhatt | February 16 2011 17:29
One more question:
3. Will 4.6.2 support Macro parameters for razor?
Sebastiaan Janssen | February 16 2011 17:46
Hey Pinal,
1. The Umbraco team is hoping for a beta by the end of this week.
2. Not exactly, but you will want to as you want all of the intellisense goodness and want to move forward. Shouldn't be a lot of work.
3. Yes it will!
Pinal Bhatt | February 16 2011 17:59
Thanks a lot Sebastiaan.
Nate | February 16 2011 19:21
Question 1) Is it possible to just use linq instead of that tiny XML parser to get that crop out?
Question 2) How is your visual studio project setup to do this kind of work?
Thanks! Great stuff!
Sebastiaan Janssen | February 16 2011 19:31
Hi Nate,
1. You'd have to first do some Linq to XML magic to do that, I suppose someone will write an awesome helper for that one of these days.
2. This blog post is what I still use, but now with VS2010.
Hendy | February 17 2011 09:17
Hi Sebastiaan,
Nice introduction to the Razor syntax :) Another helper class you might like to consider using in conjunction with Razor is the uQuery library in uComponents. It has XPath axis type methods allowing Linq queries on the Content and Media trees, and also includes methods to return specific image crops.
Cheers,
Hendy
Pinal Bhatt | February 23 2011 16:13
Hi Sebastiaan
I installed Umbraco 4.7 beta in my dev environment.
Yes now i can see that it creates .cshtml files instead of .razor but I don't see any intellisense even for the default properties.
Also looks like it still uses "Model" for current page and not "Current" as you described earlier.
Also can you tell about Parameters. Is this feature ready in 4.7 beta? if yes how to read parameter value in .cshtml.
Thanks & Regards
Pinal Bhatt
http://www.PBDesk.com