public

Prerender AngularJs templates with MVC HTML helpers

As a .NET Web developer, I'm always looking for better ways to get Angular and MVC.NET to play nicely together. Writing and rendering templates using the templateCache

9 years ago

Latest Post Deferring decisions in Evolutionary Architecture by Tim Sommer public

As a .NET Web developer, I'm always looking for better ways to get Angular and MVC.NET to play nicely together. Writing and rendering templates using the templateCache is a good example of these two world that tend to collide if not implemented correctly.

Why

Best Practice: Unless your template is very small, it's typically better to break it apart into its own HTML file and load it with the templateUrl option.

So this is not recommended:

directive('toggleButton', function () {
    return {
    	replace: true,
    	template: '<div style="float:left; cursor:pointer;">' +
    				/* ...Template Html ...  */	
                  '</div>'
    }     
});

It is recommended to place the above HTML string in a separate (cshtml) file..

I want to keep enjoying razor features like translations, Urls, model rendering though. And I don't want to serve these HTML files through Ajax either. Everything has to be rendered in the initial request.

To do this, Razor's RenderPartial comes into play.

The HTML Helper

I tend to be a DRY developer :). I am not going to write a script tag with a RenderPartial implementation for each template I define in my angular module. So I created an HTML helper that would be able to do the heavy lifting for me.

Create an 'HtmlHelpers.cshtml' file in the 'App_Code' folder of your solution, and paste the following code:

@helper RenderTemplates(string folderPath, string targetPath, params string[] excludedUrls)
{
  var folder = Server.MapPath(folderPath);
  var rootFolder = Server.MapPath("~/");
  var dir = new DirectoryInfo(folder);
  var urls = dir.GetFiles("*.cshtml", SearchOption.AllDirectories).Where(x => !excludedUrls.Contains(x.FullName));
  foreach (var url in urls)
  {
    var target = targetPath + "/" + url.Name;
    <script type="text/ng-template" id="@target" class="ng-cloak">
    @WebPageContext.Current.Page.RenderPage(string.Format("~/{0}", url.FullName.Remove(0, rootFolder.Length).Replace('\\', '/')))
    </script>
  }
}

This will allow you to call the RenderTemplates method in any razor page like this:

 @HtmlHelpers.RenderTemplates("~/app/modules/calendar/shared/templates", "/app/calendar")

The 'folderPath' variable identifies the folder on the server that contains your Angular template files. The 'targetPath' variable allows you to prefix the template names, which will allow you to identify the templates more easily.

The RenderTemplates method should now output something like the following HTML code:

    <script type="text/ng-template" id="/Angular/calendar/event.cshtml" class="ng-cloak"> 
    	/* ...Template Html ...  */
    </scipt>

You can now use this rendered template in your directive using the provided id, like this:

directive('toggleButton', function () {
  return {
    templateUrl: '/Angular/calendar/event.cshtml'
  }
});
Tim Sommer

Published 9 years ago

Comments?

Leave us your opinion.