Minimum Viable Product - Part 4 - Templates

Let's show the world our look and feel

This is part of four of a mini-series about getting an Umbraco website to market quickly. To this point we looked at the ingredients, the document types and the data types. It's now time to generate some templates.

Setting expectations

Way back in part one, we stated that Bootstrap 3 would be our responsive framework. It's not that Umbraco endorses or requires any framework, I got to choose it myself without restriction. One of the greatest things IMHO about Umbraco is the idea that HTML is not auto-generated for you. Yes this creates a bit of work upfront, but you don't have to worry about Umbraco mucking up a design that may have come from a frontender in-house or from even from a completely different agency. If you are expecting a Wordpress type experience where the menus magically appear with hundreds of behavioral options, you'll be disappointed. You'll also be likely disappointed that templates in Umbraco aren't theme-based in general. I, however, am not at all disappointed about that. I want complete control of the templating. Think of Umbraco as an awesome circuit board that just needs a fancy case around it. The circuit board handles all of the CRUD and you simply need to put window dressing on it.

Think of Umbraco as an awesome circuit board that just needs a fancy case around it. The circuit board handles all of the CRUD and you simply need to put window dressing on it.

Roll up your sleeves

Back in part two I mentioned that I subscribe to the idea that one document type = one template mapping. So for each document type that manifests as a page, we will need a template. The items meant to be data only (i.e. ContactFormSubmission, MonthFolder, YearFolder, etc) will not have templates because they are organizational bits only. Ok roll up your sleeves and let's get to work. We will need the following templates:

These should automatically show up in your Settings > Templates section. If not, just right-click and use the create dialog to add them back in. Keep in mind that when you do this, a .cshtml file named after the template name is generated on your file system. Don't forget to add these to your Visual Studio project.

There is one important template that is missing from this list. The Base.cshtml should be created in your /{webroot}/Views/ folders to encompass the global header, footer and menus to be inherited on every page as a layout. Shouldn't it be in Umbraco as a template? Well no not really. At present there is a requirement that templates need to be added to the Settings > Templates if you want to assign them to a document type. The Base template will never be directly assigned to a document type. Hopefully it all makes sense in a moment.

All about that base

So let's look at our very first code of this entire series so far, the following is our base.cshtml (pay attention to the local comments). It looks like a fairly normal Razor MVC view with standard HTML and JS:

@using G42.UmbracoGrease.G42RedirectHelper //my custom library
@using KGLLC.Umbraco.Helpers //local code found in this project
@inherits UmbracoTemplatePage //the 'normal' inherited type for this view in Umbraco
@{
    RedirectHelper.EnsureSsl(); //we could have done this with a web.config setting but I have a helper that does some custom bits of logic
}
<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8"> <![endif]-->
<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->
<!--[if !IE]><!-->
<html lang="en">
<!--<![endif]-->
<head>
    <title>@Model.Content.ToTitleTag()</title><!-- this is Umbraco stuff combined with custom code -->

    <!-- Meta -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="@Model.Content.ToMetaDescription()"/><!-- this is Umbraco stuff combined with custom code -->

    <!-- Favicon -->
    <link rel="shortcut icon" href="/assets/images/favicon.ico">

    <!-- Web Fonts -->
    <link rel='stylesheet' type='text/css' href='//fonts.googleapis.com/css?family=Open+Sans:400,300,600&amp;subset=cyrillic,latin'>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link href="/assets/stylesheets/css/font-awesome.min.css" rel="stylesheet">
    <!-- IconPicker package in use here to expose Umbraco font icons to the frontend -->
    <link href="/App_Plugins/IconPicker/css/umbraco-icons.css" rel="stylesheet" />
    <link rel="stylesheet" href="/assets/stylesheets/css/application.css">
</head>

<body id="body">
    <!-- this is regular MVC stuff -->
    @Html.Partial("~/Views/Partials/header.cshtml")

    <!-- templates that inherit this template as a layout will have their content placed here in general -->
    <!-- this is regular MVC stuff -->
     @RenderBody() 

    <!-- this is regular MVC stuff -->
    @Html.Partial("~/Views/Partials/footer.cshtml")

    <!-- our JS at the bottom to reduce blocking -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script src="/assets/scripts/jquery.parallax.js"></script>
    <script src="/assets/scripts/application.js"></script>

    <!-- this is regular MVC stuff -->
    <!-- we will use this to add scripts that are page specific -->
    @RenderSection("FooterScripts", false)

    //Google tracking here

   </body>
</html>

The base template uses a few partials for the header and footer. I'm not using a fancy menu right now, but the menu can easily be generated based on these methods. We will see some examples in a moment. I have a few custom extension methods that are mixed into the code that extend Umbraco a tiny bit.

Let's see the template for a blog post

The following is the complete template for a BlogPostPage, pay attention again to the comments as they point out some functionality:

@using Archetype.Extensions //needed for outputting our Archetype
@using Archetype.Models //same as above
@using KGLLC.Umbraco.Helpers //some code I import for some date formatting
@inherits UmbracoTemplatePage //remember this appears on most templates and gives us access to Umbraco helpers and models
@{
    Layout = "Base.cshtml"; //this is how our pages will look uniform with the header/footer being global

    //this is how we grabbed strongly typed properties from a published piece of content
    var topImage = Model.Content.GetPropertyValue<IPublishedContent>("topImage");
    var topImageUrl = "";
    
    if (topImage != null)
    {
        topImageUrl = topImage.Url;
    }
}

<!-- here is our custom JS we want to bubble up to the base.cshtml -->
@section FooterScripts {
    <script src="/assets/scripts/highlight.pack.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>
}

<section id="main" class="blog blog-page">
   <!-- more Umbraco stuffs here that allows us to get an image crop -->
    <section class="parallaxModule title" data-parallax="scroll" data-image-src="@topImageUrl.GetCropUrl(width: 1200)">
        <div class="container">
            <!-- the model is of type IPublishedContent and represents the current page -->
            <h1>@Model.Content.Name</h1>
        </div>
    </section>

    <section class="container blog-meta">
        <div class="col-md-12">
            <div class="dateline">
                <div class="col-md-6 primary-color">
                    <!-- this isn't Umbraco, this is my custom library to handle date formatting -->
                    @Model.Content.ToPublishedDateLong()
                </div>
                <div class="col-md-6 text-right">
                        
                </div>
            </div>

            <div class="tagline">
                <div class="col-md-12">
                    <ul class="list-unstyled list-inline">
                        <!-- this is taking advantage of the built-in 'tags' helper -->
                        @foreach (var tag in Umbraco.TagQuery.GetTagsForProperty(Model.Content.Id, "tags"))
                        {
                            <!-- notice here I'm setting the URL explicitly to make the 'get by tag' page to work later on -->
                            <li><a href="/blogs/tag/@tag.Text.ToUrlSegment()" class="btn btn-default">@tag.Text.ToLower()</a></li>
                        }
                    </ul>
                </div>
            </div>
        </div>
    </section>

    <!-- whoa! this bit maps each of our Archetype modules to a partial view and render each one in order -->
    @Html.RenderArchetypePartials(Model.Content.GetPropertyValue<ArchetypeModel>("modules"))
</section>

Demystifying Archetype

Archetype is able to output all of your sections because of the RenderArchetypePartials method. In the end it's just a loop that maps the alias of each fieldset to a partial file name found at `/{webroot}/Views/Partials/Archetype`.

If we look at the RichtextModule, the template is very simple:

@inherits UmbracoViewPage<Archetype.Models.ArchetypeFieldsetModel> //notice this line is different from a normal view as it has a different model

<section class="container richtext-module">
    <div class="col-md-12">
        @Html.Raw(Model.GetValue<string>("text")) //this is specific to Archetype not Umbraco
    </div>
</section>

And for completeness, here are the other three templates for the Archetype modules.

Let's break the internet

So now that I've thrown some templates in front of you, you might be wondering about all of that Umbraco specific code that grabs data from a piece of content and outputs it to the template. I have some good/not-so-good news for you. There are at least four ways to get data from a data type in version 7 of Umbraco. Good because you have options, not so good because you may get confused when you google some of this. Let's look at the ways that I know of to get data from Umbraco piped to your templates:

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

//method 1 - IPublishedContent interface with strongly typed results
@Model.Content.Name //gets the name of the current page
@Model.Content.GetPropertyValue<int>("someIntProperty") //gets some int property and returns an int

//method 2 - Dynamics with type resolved at runtime
@CurrentPage.Name //gets the name of the current page
@CurrentPage.SomeIntProperty //gets some int property as a dynamic

//method 3 - Umbraco Helper returns IHtmlString
@Umbraco.Field("someIntProperty") //gets some int property as a HTML string

//method 4 - Change the model to something other than IPublishedContent
//note that you'd have to change the model inheritance at the top of this
@Model.Foo //Custom Property
@Model.Bar //Custom property

So which should you use?

Depends of course. I would highly recommend method 1 for speed to market however option 4 is the most MVC way and something we should all strive for as we evolve as developers. The drawback to method 4 is the extra coding and skill level it takes. Method 2 and 3 are quick and easy as well, but as you'll find they are very limited when it comes to built in functionality. Take for instance with method 1 (and method 4) combined with the third-party package Core Property Value Converters. Together you can get to your data quicker and much more elegantly, let's look at some more code:

//method 1
@Model.Content.GetPropertyValue<string>("someMntpCsvValue") //outputs a CSV list such as: '1234,5678,9012' which requires further processing to get to the actual IPublishedContent that you are after

//method 1 with core PVC package
@Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("someMntpCsvValue") //outputs a list of IPublishedContent

//method 4 can wrap this functionality in a custom model

//method 2 and 3 
@Umbraco.SomeMntpCsvValue // will always yield a CSV that requires further processing
@Umbraco.Field("someMntpCsvValue") // will always yield a CSV that requires further processing

What may not be obvious in the code above is that when using method 1, the Umbraco core processes the data through installed property value converters. Method 2 and 3 do not get passed through these converters as far as I know (with possibly method 3 going through a single one). So to wrap up how to get data, use method 1 (or 4) and install/use the Core Property Value converters (should be included in the core IMHO).

If you wish to go with method 4 because you're a superstar, you'll want to checkout hijacking content, the Hybrid Framework by Jeroen Breuer, Ditto by Lee Kelleher, Matt Brailsford, James Jackson-South & others and Model Builder by Stephan Gay. Dave Woestenborghs has a great article here regarding the differences between Dynamics and Strongly Typed in Umbraco.

Let's take a final look at the structure of how my views are usually organized. Your mileage may vary but in general you will see a lot of projects organized this way. I've included the Home, ErrorPage and AboutMe template code for you to look at but I'll be holding off on the ContactForm, TagPage and SearchPage as they'll appear as their own articles soon. 

Summary

Ok, this was a bunch of information to drop on you. Bottom line is you have 100% markup control, several ways to get the data from a data type onto a template and an idea of how to structure your views. The patterns you've been presented are all you should need to build an amazing looking/functioning Umbraco site. You should be working with IPublishedContent when working with templates (or a model that exposes it). These methods are your keys to success and you need to get to know them well. Next up, I will be covering how the 'get by tag' functionality works in part 5.