If you haven't read our previous blog entry on MVC Umbraco, I strongly suggest that you read it because this entry follows up on what has already been discussed.
If you remember well, we had built an abstract layer above and independent from Umbraco CMS which translates Umbraco document types into models, which are then used by the website's controller and subsequently displayed in the view. It has also been established that the major advantages in using this layer is that Umbraco could still be updated (unless it is a major overhaul) without involving any changes from our end and that it is highly flexible, allowing us to combine an easy-to-use content management system together with tailor-made web development.
Last blog entry we also concluded with a working example, however this was very simple. This week we will extend this example by adding more complex document types and creating HTML helper methods which convert the Umbraco model into a chunk of HTML code ready to be used in your view.
The first Umbraco document type we will be exploring is one representative of a blog post, such as this page right here. The properties which we have agreed that will be included in every blog post will be the following (as always, you may create your own Umbraco document types with no virtually restrictions):
- META Title
- Page Title
- Summary
- HTML
- Thumbnail Image
- Date Published
As you may note, many of these properties are also included in the Page document type, we have discussed last time, with only two of these being new properties. Therefore in Umbraco we could set the Blog to be a child of the Page document type, allowing it to inherit its properties. The Page model has also been created previously (PageViewModel.cs) and with it so has the definition and construction of its properties. And this is where what you have learnt at University (or wherever you learnt your web development principles) comes in handy.
Do you remember inheritance and polymorphism? I would hope so, otherwise consider revising your basics before aspiring to be a good web developer. From Wikipedia: inheritance is a way to establish is-a relationship between objects. Therefore in our case, an instance of a Blog is-an instance of a Page. Also from Wikipedia: Polymorphism is the ability to create a variable, a function, or an object that has more than one form. In our example, an instance of a Page could be a Page or it could also be a Blog, depending on the constructor we have used. But let's put this to practice:
public class BlogViewModel : PageViewModel { public DateTime DatePublished { get; set; } public string DocumentImage { get; set; } public string DocumentImageAlt { get; set; } public int DocumentImageWidth { get; set; } public int DocumentImageHeight { get; set; } public BlogViewModel() : base() { } public BlogViewModel(Node node) :base(node) { DatePublished = node.GetProperty("datePublished") == null ? DateTime.Now : DateTime.Parse(node.GetProperty("datePublished").Value); DocumentImage = new Media(node.GetProperty("imageThumbnail").Value)).getProperty("umbracoFile"); DocumentImageAlt = new Media(node.GetProperty("imageThumbnail").Value)).text; DocumentImageWidth = new Media(node.GetProperty("imageThumbnail").Value)).getProperty("umbracoWidth"); DocumentImageHeight = new Media(node.GetProperty("imageThumbnail").Value)).getProperty("umbracoHeight"); } }
Here we have declared the BlogViewModel object which inherits from PageViewModel as already explained. We then have declared five new properties, DatePublished and DocumentImage, DocumentImageAlt, DocumentImageWidth, DocumentImageHeight.Assuming you are familiar with Umbraco, you should then know that images are uploaded in the Media section and each Media item of type Image contains the above four properties (as well as the extension and number of bytes, but these are irrelevant for this blog post). In the constructor we are then creating a new instance of Media and reading the respective properties, where umbracoFileis the URL of the image.
In the controller nothing has changed as we simple create an instance of BlogViewModel passing the blog node as a parameter.
BlogViewModel model = new BlogViewModel(Node.GetNodeByXpath("root/Blog"));
In the view, everything has remained the same, except now we will be calling our new properties, including the thumbnail image.
<div class="content"> <h1>@Model.PageTitle</h1> <img src="@Model.DocumentImage" alt="@Model.DocumentImageAlt" width="@Model.DocumentImageWidth" height="@Model.DocumentImageHeight" /> <span class="date">@Model.DatePublished</span> @Model.HTML </div>
As you can notice, creating the model and HTML for an image is quite tedious at best; and considering this will be used and re-used many times on different views and for different models, it made sense to create another view model and an HTML helper. Starting with the new view model, we have added the following class to our layer:
public class ImageViewModel { public string ImageUrl { get; set; } public string AlternativeText { get; set; } public int Width { get; set; } public int Height { get; set; } public ImageViewModel(Media media) { // src this.ImageUrl = media.getProperty("umbracoFile") == null ? String.Empty : media.getProperty("umbracoFile").Value.ToString(); if (!String.IsNullOrEmpty(this.ImageUrl)) { // alt this.AlternativeText = media.Text; // width this.Width = media.getProperty("umbracoWidth") == null ? 0 : int.Parse(media.getProperty("umbracoWidth").Value.ToString()); // height this.Height = media.getProperty("umbracoHeight") == null ? 0: int.Parse(media.getProperty("umbracoHeight").Value.ToString()); } else { AlternativeText = String.Empty; Width = 0; Height = 0; } } }
As you can see we have created a view model containing the properties declared in the BlogViewModel, which in turn will be updated to utilize this newly created model, hence updating the DocumentImage as follows:
public ImageViewModel DocumentImage { get; set; }
Going on to the view, an HTML helper is a feature in ASP.NET MVC similar to the traditional ASP.NET user control. By creating your own helper, you are able to output the required HTML to the view whenever the helper is called. By default, ASP.NET MVC already includes HTML helpers and you probably have used these already, such as the following helper used to create an <a> tag:
@Html.ActionLink("About this Website", "About")
In our case, we want to create an HTML helper for Umbraco images. There are numerous tutorials and plenty of information available on the web on how to create an HTML helper, so I will not go into the details, however it is neccessary to understand that we will be passing the ImageViewModel and converting this into an MvcHtmlString.
/// <summary> /// Builds an <img> tag from the given UmbracoImageViewModel /// </summary> /// <param name="umbracoImage"></param> /// <returns>Empty string if no UmbracoImageViewModel was given</returns> public static MvcHtmlString UmbracoImageFor(this HtmlHelper helper, ImageViewModel umbracoImage, string id = "", object htmlAttributes = null) { if (umbracoImage != null) { // create the <img> tag var tagBuilder = new TagBuilder("img"); // create an id if (!String.IsNullOrEmpty(id)) { tagBuilder.GenerateId(id); } // attributes tagBuilder.MergeAttribute("src", umbracoImage.ImageUrl); tagBuilder.MergeAttribute("alt", umbracoImage.AlternativeText); tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); return MvcHtmlString.Create(tagBuilder.ToString()); } return MvcHtmlString.Create(""); }
And finally the simplest step, calling this HTML helper in our view
@Html.UmbracoImageFor(item.DocumentImage)
And now we are one step closer to building our abstract Umbraco-to-MVC layer.