Don't worry, this post isn't about cooking, it's about the many different ways there are to render ModelsBuilder models in an Umbraco website.
I've been working on a website which uses ModelsBuilder and there is content which gets rendered out, using the same markup, in several different places on the site. I wanted to be able to write the markup code once and call it from all the different places, passing in the ModelsBuilder model which has the data in it.
In this example, the ModelsBuilder model is a class called Article, I'm rendering out a summary of the article in multiple places around the site. The Article summary has an Image, Title, Summary and Link to the article like this:
<div>
<img src="/media/1001/article-1.jpg" />
<h2>The article title here</h2>
<p>The summary of the article here</p>
<a href="/news/article-1/">Read more</a>
</div>
I created a poll on Twitter
I couldn't decide on the best way to make this markup reusable, so I created a poll on Twitter to find out how other people render their ModelsBuilder models.
I thought it would be interesting to go through them giving you the pros and cons for each of them, and hopefully it might teach you something new or remind you about some of the different ways to do it. It helps to know different ways so you can identify it when you see it on other projects that other people have worked on before you.
We'll go through the main ones from the poll first and then we will look at the ones people suggested in the comments. By the end of it we will have a good list of ways to do it, with simple examples and pros and cons.
You can use these links to skip to the different solutions:
- Partial View
- Html.DisplayFor()
- App_Code Shared Helper
- Surface Controller Action
- Html Helper Extension
1. Partial View
@Html.Partial("Partials/_Article", article)
What is it?
A partial view is a reusable view which can be used as a child view in other parent views. You can pass a model to the partial view, to render out the content of the model.
Pros
- It is the most common way to render reusable markup and is easiest to get started with. You only need to know the basics of MVC.
- You can easily improve performance by changing it to be a cached partial like this@Html.CachedPartial("Partials/_Article", article, 3600)
Cons
- You have to specify the path of the partial view to use, wherever you use it. Wouldn't it be good if it just knew already which view to use to render it?
- There is no checking of the partial view at compile time, meaning if you have typed something wrong you won't see the error until you look at the page at runtime in the browser.
How to do it
- Create a partial view called _Article.cshtml in ~/Views/Partials/
@model ContentModels.Article
@using ContentModels = Umbraco.Web.PublishedContentModels;
<div>
<img src="@Model.Image.Url" />
<h2>@Model.Title</h2>
<p>@Model.Summary</p>
<a href="@Model.Url">Read more</a>
</div>
- Then you can call it like this:
@Html.Partial("Partials/_Article", article)
2. Html.DisplayFor()
@Html.DisplayFor(m => article)
What is it?
Display Templates are partial views which are bound to a particular class model.
Pros
- Looks very clean
- MVC convention
Cons
- Sometimes it is hard for other developers to find where the partial view is located if they are not used to this convention.
- You need to adhere to specific naming conventions, e.g. name the partial the same as the model name. This might go against your in-house naming conventions.
How to do it
- Create a folder under Views called DisplayTemplates
- Create a partial view inside this folder and make sure it is named the same as your model, but with the extension of a normal view so mine would be Article.cshtml
@model ContentModels.Article
@using ContentModels = Umbraco.Web.PublishedContentModels;
<div>
<img src="@Model.Image.Url" />
<h2>@Model.Title</h2>
<p>@Model.Summary</p>
<a href="@Model.Url">Read more</a>
</div>
- Then you can call it like this:
@Html.DisplayFor(m => article)
3. App_Code Shared Razor Helper
@SharedRazorHelpers.RenderArticle(article)
What is it?
In MVC you can put a helper in your view to render a common block of code, but to have it reusable across the whole site, you can put it in the App_Code folder.
Pros
- Compile time error checking. You will see any errors in Visual Studio when you try and build the project, as opposed partial views where errors only show up at runtime.
Cons
- Old way of doing things
- Not as performant as the other options
If you have razor helpers or functions in your views, you can put them in a central place, in the App_Code folder so that you can call them from any view within the web project. Like this:
How to do it
- Create a view in the App_Code folder called SharedRazorHelpers.cshtml
@model ContentModels.Article
@using ContentModels = Umbraco.Web.PublishedModels;
@helper RenderArticle(ContentModels.Article article)
{
<div>
<img src="@model.Image.Url" />
<h2>@model.Title</h2>
<p>@model.Summary</p>
<a href="@model.Url">Read more</a>
</div>
}
- Then you would call it like this:
@SharedRazorHelpers.RenderArticle(article)
4. Surface Controller Child Action
@Html.Action("RenderArticle", "ArticleSurface", article);
What is it?
A surface controller child action allows for a controller to execute for a portion of the rendered area of a view. They are ideal for reusable code.
Pros
- You can use it to execute additional business logic which doesn't belong in the partial view
- You can easily add donut caching
Cons
- There is more plumbing code to get this set up, rather than just calling the partial view and passing the model
How to do it
- Create a SurfaceController like this:
using System.Web.Mvc;
using Umbraco.Web.Mvc;
using ContentModels = Umbraco.Web.PublishedModels;
namespace Skrift.Web.Controllers
{
public class ArticleSurfaceController : SurfaceController
{
[ChildActionOnly]
public ActionResult RenderArticle(ContentModels.Article article)
{
return PartialView("~/Views/Partials/_Article.cshtml", article);
}
}
}
- Create a partial view called _Article.cshtml in ~/Views/Partials/
@model ContentModels.Article
@using ContentModels = Umbraco.Web.PublishedContentModels;
<div>
<img src="@Model.Image.Url" />
<h2>@Model.Title</h2>
<p>@Model.Summary</p>
<a href="@Model.Url">Read more</a>
</div>
- Then you can call it like this:
@Html.Action("RenderArticle", "ArticleSurface", article);
5. Html Helper Extension
@Html.RenderArticle(article)
What is it?
An Html Helper Method is a method which returns a string or IHtmlString, usually. You can add extension methods to the Html Helper and you can get it to return a partial view.
Pros
- You don't need to specify the view path
- The code looks cleaner than the other ways
Cons
- More plumbing code to get this set up, rather than just calling the partial view and passing the model
How to do it
- Create an Html Helper Extension Method in a static class
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Umbraco.Web.PublishedModels;
namespace Skrift.Web.Extensions
{
public static class ArticleExtensions
{
public static HtmlString RenderArticle(this HtmlHelper helper, Article article)
{
return helper.Partial("~/Views/Partials/_Article", article);
}
}
}
- Create a partial view called _Article.cshtml in ~/Views/Partials/
@model ContentModels.Article
@using ContentModels = Umbraco.Web.PublishedContentModels;
<div>
<img src="@Model.Image.Url" />
<h2>@Model.Title</h2>
<p>@Model.Summary</p>
<a href="@Model.Url">Read more</a>
</div>
- Then you can call it like this
@Html.RenderArticle(article)
Summary
I personally like the Html.DisplayFor approach, but it can be hard for new people coming into the project to find the partial views if they aren't used to the convention of DisplayTemplates and EditorTemplates.
I also like the App_Code approach because if you have any errors with your C# code in the view it will give you an error when you try to build in visual studio. This isn't the case with normal partial views.
So, which should you use?
It is personal preference but maybe partial views are the most industry standard so it might be worth sticking with that. But if you haven't tried some of these approaches listed above, give them a go with an open mind and see which you think you like best and share your findings and maybe this article with the rest of your team.