How To Create A Global View Model For Your Umbraco Layout File

When working with MVC, it is best practice to work with strongly-typed Models to separate your presentation logic from your business logic.  I’ve talked previously about how to create models to represent document types, but that still leaves the problem of how do you create strongly-typed models for your websites global properties, like your header and footer.  One option is creating a base controller to create them, another option is creating a base page.  The problem with this approach is you’re combining your page models with your global models.  A better approach would be to have all the properties and data you need in your layout.cshtml, header and footer partial populated automatically outside of your page/controller.

In today’s guide, I’m going to go over a lot of codes so be warned!  By the end of this guide, we will have created a global view model for our layout file which will be populated with all the data the layout file needs, so you don’t have to worry about setting it up in your controllers ever again.

IResultFilter

The first part of this process will be implemented using an IResultFilter. A ‘result filter’ is a custom class we can create, that contains logic that will be executed before and/or after a view is loaded.  For example, we can use this to populate a layout ViewModel on any page request for all of our page controllers.

To get an IResultFilter to get triggered before a view is rendered, we need to register it in the global.asax. That code will look like this:

protected override void OnApplicationStarted(object sender, EventArgs e)
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LayoutViewModelResultFilter());
}

If you installed Umbraco via Nuget, then by default your global.ascx might not trigger automatically; I will cover how to set-up your web.config in a future post. The next step is to create the IResult filter:

public class LayoutViewModelResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
var model = filterContext.Controller.ViewData.Model;
var layoutModel = model as ILayoutViewModel<RenderModel>;
if (layoutModel != null)
{
var layout = new LayoutViewModel();
layoutModel.Layout = layout;
}
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
}
}

Let’s talk through what this is doing.  First, I’m getting the current page request object.  Next, I’m checking that the current model implements ILayoutViewModel.  I’ll cover this part in more detail later, but for now just think of this as a base view model that we will be passing into the Layout.cshtml.

If the page request does this, we populate the view model with the header, footer data etc.. and then pass it back to the MVC pipeline to render it in the view.

Base View Model

So for the first part let’s define ILayoutViewModel.

public interface ILayoutViewModel<out T> where T : RenderModel
{
LayoutViewModel Layout { get; set; }
T CurrentPage { get; }
}

What we’re saying here is we will create an interface that takes in a RenderModel.  RenderModel is an Umbraco model object that defines the basic properties all Umbraco document types/pages will define.

We also define a CurrentPage property that will be used to store the current pages data.

Base View Model

Next we will define out Base View Model.  After we set this up, you will always have to pass back a view model to your views, otherwise you’ll get an object casting issue.

public class BaseViewModel<T> : ILayoutViewModel<T> where T : RenderModel
{
public BaseViewModel(T currentPage)
{
CurrentPage = currentPage;
}
public T CurrentPage { get; private set; }
public LayoutViewModel Layout { get; set; }
}

The code for the model is hopefully pretty self-explanatory, it implements the interface and it’s properties.

The Layout View Model

The next step is to define a layout view model. This file will be custom to your solution.

public class LayoutViewModel
{
public HeaderViewModel Header
{ 
get
{
var helper = new UmbracoHelper(UmbracoContext.Current);
return new HeaderViewModel(helper.TypedContentAtRoot()
.First(x => x.Id == 1077)
.DescendantOrSelf(1)
.Children.Where(x => x.IsVisible()));
}
}
}

In this example, I’ve created a basic layout view model, that returns a header view model, .

Defining Our Page Objects

Let’s say we have a home page template, we would create a HomepageViewModel that inherits from our BaseViewModel and takes in a ‘HomeModel’.  Remember that ‘HomeModel’ needs to inherit from RenderModel.

public class HomepageViewModel : BaseViewModel<HomeModel>
{
private readonly HomeModel _currentPage;
public HomepageViewModel(HomeModel currentPage)
: base(currentPage)
{
}
}

Page Model

I’m skipping all the page related to model code, so the basic page will look like:

public class HomeModel : RenderModel
{
public HomeModel()
: base(UmbracoContext.Current.PublishedContentRequest.PublishedContent)
{
}
}

Note, that I’m passing in ‘UmbracoContext.Current.PublishedContentRequest.PublishedContent’ to the base constructor. I’m using this approach so I can have a parameterless constructor.

The Controller Code

The next step is

public ActionResult Index(RenderModel model)
{
var viewModel = new HomeModel();
var homepageViewModel = new HomepageViewModel(viewModel);
return CurrentTemplate(homepageViewModel);
}

In our controller, we create a new strongly typed model for our document type and pass that into the View Model.  I then pass the View Model back to the view.

The HTML

The next steps are defining the layout.cshtml and the home pages .html.

@model SampleSite.Interfaces.ILayoutViewModel<Umbraco.Web.Models.RenderModel>
@using SampleSite.Models;
<!DOCTYPE html>
<html>
<head>
<title>@Model.CurrentPage.Content.Name</title>
</head>
<body>
@Html.Partial("Header", @Model.Layout.Header)
@RenderBody()
</body>
</html>

The most import thing to note is the @model. In here we’re implementing ILayoutViewModel.  This is the interface we define in the IResultFilter. The ILayoutViewModel is also the interface that our Base View Model implements from.

Our Home page view looks like this:

@model SampleSite.ViewModel.HomepageViewModel
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
MAIN PAGE

We use the HomepageViewModel as the model, define the Layout.  On our page, we can then implement any logic we want.

Conclusion

In today’s guide, we’ve covered a lot of code… let’s re-cap.  By now I’m hoping you get why we want to create a Strongly-type view model for our global page data, like the header and footer.  Coupling the page data with this data is a bad approach because not every page might need a header and footer.  If you have an e-commerce site, for example, it is very likely that your checkout process won’t need the standard header and footer.  If we have to implement the header and footer page on each new document type, we will also be adding a lot of duplicate codes, which is bad.

Instead, we want to use an IResultFilter, to intercept the MVC request to render a page, populate the layout view model, add it back to the request so when the ‘layout.cshtml’ the file is called, we have a layout view model that contains all the data we care about.

To do this, we first register the ReultFilter in the Global.asax, next we create the Result filter.  In the ‘result filter,’ we get the current page object and make sure it inherits from a special interface we will define to store the global data.  This interface will be implemented by a base view model, which all our page controller will use.

Next, we defined the base view model, a home page view model that implements the base view model and a home page model.  In the home page controller, we create the home page model, pass it into the home page view model, then return it.

In ‘Layout.cshtml’ we use the interface we defined in the IResultFilder

‘@model SampleSite.Interfaces.ILayoutViewModel’

Implementing this means we now have all the data we defined in the Layout View Model as well as access to the current pages model data.  With this defined, we can now do whatever HTML we want.  Our business logic is now abstracted from the view, we can use Razor, and we don’t have to worry about setting this data anymore.  In each controller as long as you pass back a view model that inherits from ILayoutViewModel, the header and footer will be automatically populated for you.

Jon D Jones

Software Architect, Programmer and Technologist Jon Jones is founder and CEO of London-based tech firm Digital Prompt. He has been working in the field for nearly a decade, specializing in new technologies and technical solution research in the web business. A passionate blogger by heart , speaker & consultant from England.. always on the hunt for the next challenge

More Posts

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *