Getting started with MVC in Umbraco 4.10

Tuesday, October 30, 2012 by Peter Gregory

Shazwazza

For those that have been following the Umbraco Roadmap closely would have noticed "MVC Support" in the list of features for the upcoming v4.10 (currently in BETA and can be downloaded from codeplex).  Why did we add MVC support to Umbraco? Other than the standard MVC separation of concerns, one of the main advantages of using MVC is that you will be able to use Razor everywhere and not just in your Macros.

Prepping the Umbraco back office for MVC.

By default Umbraco 4.10 only knows about WebForms. On the surface its not too obvious how to put the Umbraco back office into MVC mode. 

To switch on MVC features we need to modify the ~/config/umbracoSettings.config file.  Scroll down the file to about half way and you will come across a key called templates with a child key called defaultRenderingEngine.  You will need to change this keys value from WebForms to Mvc.


<defaultRenderingEngine>Mvc</defaultRenderingEngine>

This will put your Umbraco 4.10 install into MVC mode, and make the back office aware that you are working in MVC.

Creating Your first View

Now that you have Umbraco configured to use MVC we want to get started building a website. Nothing is different from a standard Umbraco website, where you begin with your document types, but now, rather than the system creating MasterPages in the templates area, it is creating MVC Razor views. By default, Views created by Umbraco look like this.


@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = null;
}

 The first line basically wires up your view to Umbraco and gives you access to Umbraco features such as the Umbraco Helper (more on this later). The Layout = null; line is just like MasterPage inheritance in WebForms that allows you to set what the parent View is. Template inheritance is as easy as it was in WebForms and we will explain how to do this later.

Your HTML markup goes in below the closing }. 

You may have noticed that there are very few options on the template editor toolbar. This is because MVC features are a work in progress across a number of versions, and as features become available, this toolbar will become more populated with options such as insert Macro, insert Partial View.

What is the Umbraco Helper?

Retriever

The Umbraco Helper is just that, a helper that you can call on to retrieve things from Umbraco, to perform tasks like Searching the content index, rendering Macros, stripping HTML from text, finding out if a content item is protected etc. Generally useful and commonly used tasks that would otherwise take you a long time to build yourself. 

To access the helper in your Views is easy.  Just use @Umbraco.  If you are working in Visual Studio and you you will get Intellisense and it will allow you explore all of its options.  

Here is a quick example of how you would use the @Umbraco helper to perform a search on the Umbraco content index.


<ul class="search-results">
@foreach(var page in Umbraco.TypedSearch("mvc")){
<li><a href="@page.NiceUrl">@page.Name</a></li>
}
</ul>

 Or here is an example of how to use the helper to truncate (shorten) some text.


@Umbraco.Truncate(Model.Content.GetPropertyValue("bodyText"),200)

So how do I add page properties?

There are a number of ways to put your content properties on the page, through the Umbraco helper, through the strongly typed Model.Content, or via the Dynamic CurrentPage.  There are advantages to each.


@Umbraco.Field("alias")

This is the way that the insert Umbraco Page Property button will put your properties on the page.  The helper has all the options that you are used to with the WebForms <umbraco:item> tag such as fallback fields, insert before, insert after etc.


@Model.Content.GetPropertyValue("alias")
@Model.Content.GetPropertyValue<Type>("alias")
@Model.Content.GetProperty("alias").Value

This is how we access our properties through the strongly typed IPublishedContent (@Model.Content) representation of our Umbraco content.  The main advantage is that because its strongly typed, you get intellisense in VS, and you also can write linq against it.  This is probably my preferred way to work with Umbraco content when developing views in VS.


@CurrentPage.Alias

If you have been using Razor via dynamicNode you would probably have used the similar dynamicNode derivative. @CurrentPage is basically the same.  It is a dynamic representation of your Umbraco content (DynamicPublishedContent or the Dynamic version of IPublishedContent).

What about template inheritance?

Create a new template and then set the Layout value to the filename of the parent template. For example to set a View to have the parent View of _Layout.cshtml we would modify it to be as follows:


@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "_Layout.cshtml";
}

Lets take a look at what your _Layout.cshtml template might look like as we need a couple tags to tell our child templates where to put their content. 


@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
<!DOCTYPE html>
<html>
<head>
<title>@Model.Content.Name</title>
@RenderSection("head",false)
</head>
<body>
@RenderBody()
</body>
</html>

There are two very important tags on this template which are standard Razor View tags.  They are @RenderSection("head",false) and @RenderBody()

@RenderSection("head",false) is the equivilant of the <asp:contentPlaceHolder runat="server" ID="head"> that you would use in WebForms MasterPages.  It specifies a named section where a child template can place content.  The boolean value specifies if it is required for a child template implement this section.

@RenderBody() is a catch all type tag.  Basically it will render any content from a child template that has not been placed inside a named section.

Lets look at how a child template might be formatted to use this parent View.


@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "_Layout.cshtml";
}

@*this content will be rendered in the named section head*@
@section head{
<script type="text/javascript">
alert("Iz in yo head");
</script>
}

@*The following content will be caught by the RenderBody() tag in the parent template*@
<h1>@Model.Content.Name</h1>
@Html.Raw(Model.Content.GetPropertyValue("bodyText"))

What about Macros?

Working with MVC is a little different than working with Umbraco in WebForms mode.  You will find that most of the time you will get away with not having to create Macros at all as you can either write the Razor directly in the template or include a Razor Partial View. Macros with MVC will most likely only be needed when you want to allow editors to insert functional blocks into the rich text editor or when you need to include a form (Child Action Macro) in the page.  

Tooling for inserting Child Action Macros and Partial View Macros will be coming in v4.11.

With all that to say if you have an existing Razor or XSLT or UserControl macro that does not require any postback (ie no forms) there is an Umbraco helper method to include your macro.


@Umbraco.RenderMacro("myMacroAlias", new { name = "Ned", age = 28 })

The anonymous object (new{}) passed in are your macro parameters.

But you're using MVC so lets look at Partial Views.

The power of the Partial View

We are building our site using MVC so with MVC we also get access to Partial Views.  So for instance we could build our site navigation as a Partial View. To build a partial view they live in the standard MVC Partials folder ~/Views/Partials folder.   At the moment there is no tooling for creating a Partial view in the Partials folder via the back office.  If you must create it via the back office you can just create them in the template section which means they are just in the ~/Views folder which is ok.

Lets look at a typical navigation Partial View and then dissect it.


@inherits Umbraco.Web.Mvc.UmbracoTemplatePage

<ul>
@foreach(var page in Model.Content.AncestorOrSelf(1).Children.Where(x => x.IsVisible()))
{
<li><a href="@page.NiceUrl()">@page.Name</a></li>
}
</ul>

Notice that it inherits the class Umbraco.Web.Mvc.UmbracoTemplatePage. We do this is so we can get access to the Umbraco helper and the the Current Page and other Umbraco specific features.

To include this Partial on our page we use the following syntax.


@Html.Partial("Navigation")

This tells MVC to render a partial called navigation.cshtml passing it the Model to use.  In our case it is the IPublishedContent representation of the current page.

If you want to render a Partial View with a custom Model we can use the following inheritance and still have access to the Umbraco helper.


@inherits Umbraco.Web.Mvc.UmbracoViewPage<YourCustomModel>

To insert this partial we would do the following


@Html.Partial("Navigation", YourCustomModel)

Sometimes you will want to pass parameters to your Partial View.  This is done through the ViewData dictionary.  ViewData is an object that allows us to store and retrieve values for the current request.

So lets say we wanted to pass a value to a partial we would do it like this.


@Html.Partial("YourPartialName", new ViewDataDictionary{{ "age",33},{"name","peter"}})

And then in your Partial you can retrieve the values using: 


ViewData["age"]

Go to it!

ikea

That should be enough to get you going with MVC under v4.10 BETA.  This was only a simple primer and doesn't go into querying or how to build forms etc but we will cover this in future posts and there is already a lot of documentation written for the new 4.10 features over at GitHub that can fill in some gaps.  Also you might want to check out this blog post by Shannon on his personal blog that gives a little more information on some of the other more technical aspects of MVC features of 4.10.

Please also be aware that currently the MVC features are a work in progress and there is still a lot of tooling to be built to support it in the back office that will appear in future versions.

This is a BETA, we need your help!

4.10 is a massive step forward for Umbraco and besides the new MVC features there are loads of bug fixes and small tweaks that need testing. Unit tests only go so far and will not always catch everything. We are also "dog fooding" this release on a few internal projects but we might not catch everything either. The more real world developers we have helping to identify issues the better the final release will be. So this is a call to anyone who has some spare time or is considering starting a build on the BETA please provide your feedback and report issues at issues.umbraco.org and help make this one of the best releases yet!

UPDATE 5/11/2012: an issue has been identified in the RC that causes a YSOD when you try to pass an IPublishedContent obejct to a partial (not menitoned in this blog post but could frustrate some developers trying it out). 

For instance if you were iterating over some child pages and calling a partial for each child item.


@foreach(var page in Model.Content.Children){
@Html.Partial("somePartial",page)
}

And then your partial inherits like this


@inherits Umbraco.Web.Mvc.UmbracoTemplatePage

it would throw a YSOD saying:

The model item passed into the dictionary is of type 'Umbraco.Web.Models.XmlPublishedContent', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.

This is due to the Partial expecting a RenderModel.  The good news is that this has now been fixed! and will work as expected in the release.

31 comment(s) for “Getting started with MVC in Umbraco 4.10”

  1. Gravatar ImageStephan Says:

    This is just great!

    Another reason for using macros is the cache... so, you might want to explain how to cache the partial...

  2. Gravatar ImageKevin Lawrence Says:

    This is just awesome, 4.10 is an exciting release for me, I've waited so long to port sites over to MVC, and of course port them back from 5.2 which is doooog slow!!

    Keep up the good work guys, things are really shaping up to what is already an outstanding product.

  3. Gravatar ImageKenny Burns Says:

    Awesome work guys! I think the work and updates that have gone into Umbraco since CG and the whole v5 thing have been nothing short of fantastic. Off to download 4.10 beta and have a little play about! :)

  4. Gravatar ImageBiagio Says:

    Caching?

  5. Gravatar ImageBo Damgaard Mortensen Says:

    Great writeup! Can't wait to play around with Umbraco in MVC mode.

    Also, I think it's brilliant that we are able to pass in a custom model to the partials :-) Makes it easy to extend with custom datasources.

    As for caching, you could potentially set up your own caching (using the standard .NET caching tools) in your partials, but I agree that having it as a setting on the partial itself would be great.

  6. Gravatar ImageBertrand Le Roy Says:

    Very nice Razor support, but I'm a little confused about how this is MVC, as the post only shows V, not M or C. Specifically, Mvc almost sounds like MVC is treated like a view engine, and Umbraco.TypedSearch("mvc") looks out of place in the view, if this really is MVC.

  7. Gravatar ImageBertrand Le Roy Says:

    Comment module ate the "defaultRenderingEngine" tags around "Mvc".

  8. Gravatar ImageMike Says:

    Impressive, HQ has shown great resolve since CodeGarden, a turn around is becoming very much visible. We could have been at this point with v4 a year ago, had it not been for v5, but as long as progress is visible it's a good thing.

    Agree with Bertrand, not only should you blog about the C in MVC, you should also really take the opportunity to show leadership here. Give us guidance on how to write our CMS web applications using these tools. Because otherwise, people are going to do massive querying in the views, which I think is not why we need MVC support in Umbraco.

    Thanks!

  9. Gravatar ImageShannon Deminick Says:

    Hi @Bertrand & @Mike, thanks for the comments. You are totally correct that perhaps this blog post makes it seem like its just another view engine and we are just supporting razor but this is not the case. This blog post was intended as a primer for an average Umbraco user to get them started using the new and improved syntax and MVC engine. I posted up an article on my blog as Pete mentioned which you can find here: http://umbraco.com/follow-us/blog-archive/2012/10/30/getting-started-with-mvc-in-umbraco-410.aspx , i realize that even my article does not dive in to all of the technical details that you are after but it does reference all of the documentation we've currently written on the MVC implementation and its definitely worth a look.

    So to answer the M, V, and C question, i'll try to be brief and hopefully any of the other details you can find in the documentation or referenced on my blog post, otherwise please ask, comment, etc :)

    Lets start with the 'C' (controller), all pages are routed via the RenderMvcController which model binds an IPublishedContent object that is found during the routing process so you get this as a parameter. A developer using Umbraco might want full control over this process so you can do something called HIjacking Routes (can be found in the documentation or on my blog post), which uses a conventionally named Controller based on the currently executing Document Type name and if they match then those routes get executed through your custom controller (this includes custom routing to Actions for different template names).

    Next on to the 'M' (model), as I mentioned, the model is an IPublishedContent object as noted in this blog post. Of course if you are hijacking a route you can return any model that you like but the IPublishedContent object will be available as a parameter of your MVC Controller Action.

    Lastly on to the 'V' (view), during the normal rendering process without hijacking routes, views just get rendered and you don't have to worry about all this stuff. But if you want full control then you can hijack a route, return your own model and return your own view because essentially we're just routing to your controller so you can do whatever you want.

    We'll try to write up more technical blogs posts and documentation around all of this stuff soon! Someone has already asked me how to implement IoC in an MVC Umbraco implementation so I'll be writing that up shortly too. Since we are just routing to either webforms or MVC, we are not doing anything out of the ordinary, anything that you can do in regular ASP.Net MVC you can do in our MVC implementation.

    Hope this helps (for now! :)

  10. Gravatar ImageShannon Says:

    Sorry!!! wrong link to my blog... doh! here you go:
    http://shazwazza.com/post/Native-MVC-support-in-Umbraco-coming-very-soon!.aspx

  11. Gravatar ImageShannon Deminick Says:

    For those looking for caching partials like you do with macros, we've implemented that now with the same parameters (i.e. by page and by member), see docs:
    https://github.com/Shandem/Umbraco4Docs/blob/4.8.0/Documentation/Reference/Mvc/partial-views.md

  12. Gravatar ImageLars-Erik Aabech Says:

    YAY! Finally!
    Been fiddling with it the last two days, and am just super excited to get started with our next clients web with greenfield everything. :)

    Tnx a lot!

  13. Gravatar ImageTom Says:

    Just wondering is @Model going to review to a stock standard asp.net MVC ViewModel?
    i.e. if you had some class representing your ViewModel could you then use it and .Content is an extension?

    Am I understanding that correctly?

    Cheers,
    Tom

  14. Gravatar ImageShannon Deminick Says:

    Hey @Tom,
    not sure I understand your question correctly. The @Model object of the default UmbracoTemplatePage view class is of type 'RenderModel' which is our custom view model. If you want to have your views inherit from UmbracoViewPage you can pass whatever view model you want to that view and still have access to things like @Umbraco, etc... You can do this for Partial views or if you want to 'hijack a route' then you can create whatever view model you like and pass it to your view. In our 'RenderModel' view model class the 'Content' property represents an 'IPublishedContent' object.
    Not sure if this answers your question or not?
    Cheers,
    Shannon

  15. Gravatar ImagePreyash Says:

    @Stephen, Here you go about caching like macro with partial view

    https://github.com/Shandem/Umbraco4Docs/blob/4.8.0/Documentation/Reference/Mvc/partial-views.md

  16. Gravatar ImageBen Edwards Says:

    It would be really create if a helper class such as uSiteBuilder was rolled right into the next release as it works really well when you roll it youself!

    Great release though

  17. Gravatar ImageJason Says:

    Shannon,

    It doesn't appear that I can have a RenderSection() inside a section definition. Is it possible to have more than one level of parents in the templates? How would I go about doing that?


    Thanks,
    Jason

  18. Gravatar ImagePeter Gregory Says:

    @Jason Are you getting any particular errors?

    You definitely can have a section def inside a section as I am working on something currently that uses it quite a bit.

    Define your section in the View like this.

    @RenderSection("scripts",false)

    The boolean is just there to say whether or not the section must be defined in child views. False just means that its not required.

    And then in the child view that has that template set as its Layout

    @section scripts
    {
    @RenderSection("scripts",false)
    }

  19. Gravatar ImageJason Says:

    Hey Peter, thanks for the quick response!

    I'm getting this error:

    The file "~/Views/Page.cshtml" cannot be requested directly because it calls the "RenderSection" method.


    I setup my ContentMaster (top level template) exactly like you had above. Then a "Page" template that has ContentMaster as its parent with the following:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @{
    Layout = "ContentMaster.cshtml";
    }

    @*this content will be rendered in the named section head*@
    @section head{
    @RenderSection("head",false)
    }

    @*The following content will be caught by the RenderBody() tag in the parent template*@
    @Model.Content.Name
    @Html.Raw(Model.Content.GetPropertyValue("bodyText"))

    And then a Sub-Template that has this:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @{
    Layout = "Page.cshtml";
    }

    @section head{

    alert("number 2");

    }


    When I try to view the page that uses the "Page" template, I get the error.

    Thanks for the help!

    -Jason

  20. Gravatar ImageTom Says:

    There appear to be quite a few api changes.. it looks like DynamicDocument didn't make it in to 4.10.0

    Just wondering how to get access to the equivalent of DynamicNode in 4.10 as it has broken some extensions I've written.
    Cheers,
    Tom

  21. Gravatar ImageEd Says:

    I want to use some content in the _layout.cshtml file, but i can't get it to work. Must i use Partials for that or it is possible to use the page data in a _layout?

  22. Gravatar ImageTomW Says:

    +1 for Tom's comment above. I have quite a lot of instances where I use a new umbraco.MacroEngines.DynamicNode(id) and do the bulk of the querying in C#, so that all of my code isn't tied up in Razor files.

    How do I get an instance of Umbraco.Web.Models.DynamicPublishedContent or otherwise, to play with in code, compiled or otherwise, as opposed to Razor? Loving 4.10 and will love it even more once I understand the new strongly typed API

  23. Gravatar ImageRichard Terris Says:

    Excellent post - that is all!

  24. Gravatar ImageDamian Says:

    Great! Just trying this out.

    Found an error in first code sample :

    @page.NiceUrl is a method so needs to have @page.NiceUrl()

  25. Gravatar ImageOleksiy Says:

    Best CMS and excelent post, also it will be good to add choose WebForms/Mvs on install screen in future releases

  26. Gravatar ImageMintu Says:

    Great!!!!!!

  27. Gravatar ImageMartin Nielsen Says:

    Call me when there is some decent documentation on how to use my own controllers.

  28. Gravatar ImageSebastiaan Janssen Says:

    @Martin:
    Try: http://our.umbraco.org/documentation/Reference/Mvc/

    And if you have any more specific questions, make sure to ask them in the forum so the awesome community can help you out!

  29. Gravatar ImageAnthony Says:

    CurrentPage.Alias seems to return nothing, but CurrentPage.DocumentTypeAlias returns what is expected.


  30. Gravatar ImageNick Says:

    Same as Jason above I get "View x cannot be requested directly because it calls the "RenderSection" method"

    This happens if you have a master within a master within a master. I get the error on the 3rd level down of a nested template.

    I am not sure how to use templates with this crucial limitation. Is there a workaround?

  31. Gravatar ImageSebastiaan Janssen Says:

    @Nick That's all default ASP.NET MVC behavior, not something Umbraco-specific.

    I believe this is the answer here:
    http://stackoverflow.com/questions/7808377/the-file-views-position-edit-cshtml-cannot-be-requested-directly-because-it

Leave a comment