Better structure for your Umbraco Razor scripts with RenderPage and Global helpers

Together with Razor in Umbraco 4.7 comes a bunch of useful things that’s included in the WebPages assemblies. RenderPage is one of those things. It’s simply rendering another Razor script at the call:

@RenderPage("MyOtherScript.cshtml")

The other script runs in the same page and umbraco macro context which means it has access to Model as well as the Page object. So we can do this:

@{
Page.Header = "Header";
@RenderPage("MyHeaderScript.cshtml")
}

MyHeaderScript.cshtml:

<h1>@Page.Header</h1>

Another way to get the same result is by using the RenderPage-function with a data parameter:

@RenderPage("MyHeaderScript.cshtml","My header")

The parameter is an array of Object, so when I send just one object I get the value like this:

MyHeaderScript.cshtml:

<h1>@PageData[0]</h1>

See also this page for an example of a small helper which makes this code look nicer

And remember the called script also has access to Model:

MyHeaderScript.cshtml:

<h1>@PageData[0]</h1>
On page : @Model.Name

Reference RenderPage (msdn)

Note that the Page object does not live between macros, so it’s not possible to set Page.Header=”My header”; in one macro and then use it in another macro. So the scope is within one macro + and RenderPage-calls from that macro.

A simple search functionality with some separation of logic and display

Here’s a more complete example – a very simple search functionality:

MySearch.cshtml (the macro):

@RenderPage("SearchForm.cshtml")
@if (IsPost)
{ 
 @DisplaySearchResults(Request["search-string"])
 }

@helper DisplaySearchResults(string searchString){
                                                  
  var searchRoot = Model.AncestorOrSelf();
  var values = new Dictionary<string,object>();
  values.Add("keyword", searchString.ToLower());

  var searchResults = searchRoot.DescendantsOrSelf().Where("bodyText.ToLower().Contains(keyword)",values);  

  <p>Searching for <strong>@searchString</strong> resulted in @searchResults.Count().ToString() hits</p>                                                  
  
  foreach (var item in searchResults)
  {
    @RenderPage("SearchResultItem.cshtml",item);
   }  
}

SearchForm.cshtml: (no macro)

<form method="post">
Search: 
<input type="text" name="search-string" id="search-string" value="@(Request["search-string"])"/>
<input type="submit"/>
</form>

SearchDisplayItem.cshtml: (no macro)

<p>
 <a href="@PageData[0].Url">@PageData[0].Name</a>
</p>

Saving scripts in subfolders

Did you know it’s possible to save your scripts in subfolders within the Umbraco UI?

Just add a new script and enter the name together with the folder name like this:

Run your script in the Umbraco UI with the Razor Visualizer

The Razor Visualizer is a great help when you write your Razor scripts in Umbraco UI and want an immediate test of the result they produce. Just install this package and you will get a visualize button in your Razor script editor.

You can also use it to run maintenance scripts, for example to delete all documents under a certain document.

Run a razor script directly from a template without a macro

Using this syntax you can run your scripts without a macro:

<umbraco:Macro FileLocation="~/macroScripts/any-file.cshtml" runat="server" />

A sample from a current project

We have a page with course information and registration form, like this:

Header
Information about the course
Course occasion date and place
Registrated attendees
Registration Form

Currently there’s a bunch of Xslt’s handling this for us, which was kind of hard to build, and even harder to debug and change. Now we’re moving to Razor which is much easier to handle.

We decided to use this structure for the Razor scripts:

Root folder:
  Meeting.cshtml (with a macro)

  \Meeting folder:
    Information.cshtml
    Occasion.cshtml
    Attendees.cshtml
    Form.cshtml
    FormPost.cshtml

We decided to only keep scripts that have macros connected to them in the root folder, and subfolders for all related “views” – as well as scripts that performs logic (FormPost.cshtml).

The main script (Meeting.cshtml) is very simple :

@RenderPage("Meeting/Information.cshtml")
@RenderPage("Meeting/Occasion.cshtml")
@RenderPage("Meeting/Attendees.cshtml")

if (!IsPost)
{
 @RenderPage("Meeting/Form.cshtml")
}
else
{
 @RenderPage("Meeting/FormPost.cshtml")
}

Global helpers

Another way to structure the Razorness in different files is to use global helpers, that is .cshtml (or .vbhtml) files stored in the App_Code folder. Which obviously means you need to have access to that folder, and likely use Visual Studio to edit the files. (Personally I do prefer to code my Razor scripts with the help of the Umbraco UI editor.)

App_Code/GlobalHelpers.cshtml:

@helper FancyMessage(string message){
<span class="fancy">@message</span>
}

Then you can use it from your other scripts like this:

<p>And : @GlobalHelpers.FancyMessage("Hello World!")</p>

You can of course have as many global cshtml helpers as you like, and you can even store them in subfolders – then you’ll get automatic namespaces for them, so if the abouve helper was in App_Code/Fancy/Helpers.cshtml:

<p>And : @Fancy.Helpers.FancyMessage("Hello World!")</p>

Works for functions to:
App_Code/GlobalFunctions.cshtml:

@functions{
  public static string Foo (string message) {
    return message;
  }
}

}

Update: See how you can call a helper with razor code as a parameter.

@Fancy.Helpers.DivThisThat(@<i>inner contents</i>)
Advertisements

10 thoughts on “Better structure for your Umbraco Razor scripts with RenderPage and Global helpers

  1. I quite enjoy working in Visual studio, but the folders are cool as well!
    I do really like that I can seperate out my logic in @functions though, I often want reuse of parts of code as well, so I really need to put some stuff in the App_Code folder.

  2. Hmmm, I don’t get it, what’s so hard about copying a few files from one App_Code directory to another, especially if you put them in folders anyway?
    This seems to remove flexibility, if it’s all precompiled then you’ll need to recompile if you want to change something.

  3. It’s truly a great and useful piece of information.

    I’m satisfied that you simply shared this helpful information with us.

    Please stay us up to date like this. Thank you for sharing.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s