RazorEngine layouts
I am using the Razor engine https://github.com/Antaris/RazorEngine to parse the body of my email templates. Is it possible to define a layout and include other .cshtml files? for example a common header and a footer.
I got common templates and a layout working, with the help of these two posts:
RazorEngine string layouts and sections?
http://blogs.msdn.com/b/hongyes/archive/2012/03/12/using-razor-template-engine-in-web-api-self-host-application.aspx
This is my solution:
Solution 1: Layout
Used by setting _Layout
@{
_Layout = "Layout.cshtml";
ViewBag.Title = Model.Title;
}
Footer
@section Footer
{
@RenderPart("Footer.cshtml")
}
Layout.cshtml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>
<head>
</head>
<body>
<div id="content">
@RenderBody()
</div>
@if (IsSectionDefined("Footer"))
{
<div id="footer">
@RenderSection("Footer")
</div>
}
</body>
</html>
TemplateBaseExtensions
Extend TemplateBase with a RenderPart Method
public abstract class TemplateBaseExtensions<T> : TemplateBase<T>
{
public string RenderPart(string templateName, object model = null)
{
string path = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Templates", templateName);
return Razor.Parse(File.ReadAllText(path), model);
}
}
Razor Config
Set BaseTemplateType to your TemplateBaseExtensions class
TemplateServiceConfiguration templateConfig = new TemplateServiceConfiguration
{
BaseTemplateType = typeof(TemplateBaseExtensions<>)
};
Razor.SetTemplateService(new TemplateService(templateConfig));
Edit Solution 2:
If you are using a TemplateResolver. RenderPart isn't needed use the @Include instead
Footer
@section Footer
{
@Include("Footer.cshtml")
}
Resolver
public class TemplateResolver : ITemplateResolver
{
public string Resolve(string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
string path = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Templates", name);
return File.ReadAllText(path, System.Text.Encoding.Default);
}
}
Config
TemplateServiceConfiguration templateConfig = new TemplateServiceConfiguration
{
Resolver = new TemplateResolver()
};
Razor.SetTemplateService(new TemplateService(templateConfig));
Update by The Muffin Man Specify a template and render a string
var templateResolver = Razor.Resolve("Registration.cshtml");
return templateResolver.Run(new ExecuteContext());
Also I, along with others at this link https://github.com/Antaris/RazorEngine/issues/61 had issues with using _Layout
whereas Layout
worked.
'_Layout' is the old syntax. It was updated to 'Layout' in a future release.
You can easily do a lot of stuff with Razor; however, that particular project seems to abstract away a lot of the Razor engine stuff you could do (which is both good and bad). In your situation, it sounds like you would be much better off implementing your own Razor solution (it's actually not that bad) and then you can have your templates throw exceptions or pull in other content pretty easily.
For example; rolling your own solution allows you to make a base class for your razor templates which can expose the capacity to pull in "partial views" by invoking other templates. In addition, you can do model checking and throw exceptions if certain properties are null.
The easiest way to implement a layout with RazorEngine is by replacing what your template return in the @RenderBody() of the layout:
var finalHtml = layout.Replace(@"@RenderBody()", templateHtml);
E.g.:
Your _Layout.cshtml with the typical @RenderBody()
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>
<head>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
Your RazorEngine template MyTemplate.cshtml
@using RazorEngine.Templating
@inherits TemplateBase<myviewmodel>
<h1>Hello People</h1>
<p>@Model</p>
And wherever you call the RazorEngine template:
var TemplateFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "EmailTemplates");
var template = File.ReadAllText(Path.Combine(TemplateFolderPath,"MyTemplate.cshtml"));
var layout = File.ReadAllText(Path.Combine(TemplateFolderPath, "_Layout.cshtml"));
var templateService = new TemplateService();
var templateHtml = templateService.Parse(template, myModel, null, null);
var finalHtml = layout.Replace(@"@RenderBody()", templateHtml);