As an object-orientation-spoiled programmer, when you come to use an HTML rendering engine such as Razor you will soon start to wish for the same couple of design advantages that object orientation brings along.
Using polymorphism in “class-ic” object orientation, e.g., you can do things like that (simplified code snippet – don’t try copy-paste):
class Person { string FirstName { get; set; } string LastName { get; set; } virtual string FormatName() { return FirstName + " " + LastName; } } class PersonWithMiddleName : Person { string MiddleName { get; set; } override string FormatName() { return FirstName + " " + MiddleName + " " + LastName; } }
That means, you can write some generic code that iterates over a couple of Person objects and prints their names without caring how they are actually built. By default a Person would yield their first and last name when invoking FormatName, but if you need to print a middle name, too, you can simply subclass Person and override FormatName. When invoking FormatName on an instance of PersonWithMiddleName, even when it is known as Person only, it will apply the most special implementation found, here the one including the middle name.
The good news – you can get that in Razor, too
Razor has a concept that reminds me of the same type of polymorphism. Using Display Templates and Editor Templates you can define a general template to render a Person:
@* ~/EditorTemplates/Person.cshtml *@ @model Person <div class="Person">@Model.FirstName @Model.LastName</div>
This template applies each time you use EditorFor with a Person type property:
@* suppose a class Book { Person Author { get; set; } *@ @model Book @Html.EditorFor(m => m.Author)
Now say we have a book with a PersonWithMiddleName author. Then the Person template is still applied, because PersonWithMiddleName inherits from Person. But we have the option to override the template for the special class:
@* ~/EditorTemplates/PersonWithMiddleName.cshtml *@ @model PersonWithMiddleName <div class="Person"> @Model.FirstName @Model.MiddleName @Model.LastName </div>
Now, whenever the book has a PersonWithMiddleName author, this template applies. For all other Person objects, the Person.cshtml applies.
We can take it a step further! “Base templates”
If you did not know of Editor Templates before, you might be so excited about them now that you want to try them out right away and cannot think clearly enough to head on reading. Just return later.
If you already did know Editor Templates you might have been wondering: Can we take it a step further? In C# the overriding method can call its base method to reuse the code of the base class:
class PersonWithMiddleName : Person { string MiddleName { get; set; } override string FormatName() { return base.FormatName() + ", middle name: " + MiddleName; } }
Can we do that with Editor Templates?
We can, making use of this helpful answer on stackoverflow.
@* ~/EditorTemplates/PersonWithMiddleName.cshtml *@ @Html.Partial("~/EditorTemplates/Person.cshtml", Model) , middle name: @MiddleName
At a first glance you might think it looks a bit clerky but it is not: Just as a subclass needs to reference its base class with its full class name including the namespace, we reference the “base template” using its full path. We simply pass that template the same model. Thus it also uses the exact same ViewData (e.g. the “HtmlFieldPrefix”). Thus we pass it the context, which is kind of analaguous to the “this” keyword in C#.
Template Method Pattern
Now we get to an even more interesting point: If we have inheritance and polymorphism at hand, can we use any of the popular object oriented design patterns?
I tried the template method pattern. It is very useful in this place because when you render a view it is pretty likely that you want to “inject” some additional HTML in the midst of the rendering output of the base template.
Taking the example from above, let us say we want to render FirstName + MiddleName + LastName again:
@* ~/EditorTemplates/Person.cshtml *@ @model Person @{ var extension1 = ViewData.ContainsKey("ExtensionPoint1") ? ViewData["ExtensionPoint1"] : string.Empty; } <div class="Person"> @FirstName @Html.Raw(extension1) @LastName </div>
The template basically renders FirstName + LastName but provides an “extension point” for inserting HTML between the two names. A pretty silly application of the pattern, but, you know, it is an example only.
The extension must be a string containing HTML. It is only rendered if you provide it.
@* ~/EditorTemplates/PersonWithMiddleName.cshtml *@ @model PersonWithMiddleName @{ var extension1 = @RenderMiddleName().ToString(); ViewData["ExtensionPoint1"] = extension1; } @Html.Partial("~/EditorTemplates/Person.cshtml", Model) @helper RenderMiddleName() { @MiddleName }
Our specialized template makes use of the Person.cshtml “base template” for rendering the parts that the base template already knows best how to render. But it combines that with its own specializd rendering by only injecting the part that is really different from the base template – the middle name.
We don’t define the extension1 string directly but use a Razor helper instead. This allows us to make use of the Razor syntax to define the part to inject rather than writing lenghy worms of plain HTML strings.
Conclusion
Using Editor Templates (and Display Templates) in a smart combination with Html.Partial and ViewData you can leverage a (maybe) unknown range of object-oriented techniques for building your web pages, allowing for a modular, clean design in this part of your web application just like in the rest of your application, thus improving code reuse and maintainability of your templates.
I think this is great news! What do you think? Have you been using this approach before? Do you have better ideas? Or do you see any issues with this approach?
