While working to find a more optimal strategy for serving AngularJS templates as part of a Ruby on Rails app, there’s been a train of thought running through my head. The more obvious approaches are to store AngularJS templates and other client-side templates in the public folder or, with your Rails view templates, a better solution involves bringing AngularJS templates in harmony with the Rails Asset Pipeline.

A lot of what defines an optimal solution is going to depend on the purpose, size, and structure of your application. For some apps, the approach (outlined below) falls strictly under premature optimization. Based on best practices, some apps simply won’t need to worry about how client-side templates are served. For larger, repeated-use enterprise apps that make heavy use of client-side templates, it is more of a concern.

Even if it’s not your problem, let’s walk through the thought process and build an awareness of what considerations come into play when deciding how to handle serving client-side templates from within a Rails application, so read on!

Templates == Assets ... ?

If you’re coming from the Rails side of things, you might be wondering “Why would I store my client-side templates with my assets when I could keep them in the static public directory or with my other Rails view templates?” As with most things in software engineering, there isn’t one right answer, but I think both of these options tend to be more naive than they might let on.

If you’re of the mindset that client-side templates belong with your other Rails view templates, you may be right. Ultimately, the matter is application-specific, but storing AngularJS templates with your other views isn’t always a bad idea.

For example, if a template relies on a Rails controller action, or is dynamically generated for other reasons, it probably belongs with your other views. Similarly, if your app is of sufficient simplicity/complexity that it can operate as a client-side app for clients with JavaScript and as a server-side app for clients without JavaScript, you’re probably better off keeping your client-side templates with your other Rails templates.

That said, if you’re treating your AngularJS templates like your other Rails views, you may want to step back and evaluate the coupling between your user interface and your Rails back-end. Applications of this variety tend to be neither Rails applications nor AngularJS applications, but rather some kind of hideously disfigured Frankenstein that can be complex to coordinate, maintain, and extend.

Though there will always be some code required to bridge Rails and AngularJS together, I encourage you to strive for a clear distinction as to what is Rails, what is AngularJS, and what is the glue that binds the two. It is exactly this type of situation that led me to reevaluate how client-side templates should be handled in a Rails app.

On the other hand, if you’re currently serving your client-side templates from the Rails public directory, you’re in a pretty great place: You’re client-side templates are static and prefer AJAX to dynamic data injection on the back-end; Rails is minimally involved in serving the templates; and the coupling between your UI and your Rails back-end is likely minimal. At this point, the main opportunity on which you’re missing is the Asset Pipeline and the enhancements it provides for serving static assets.

Let’s take a look at some of the perks as we dig deeper into serving client-side templates with Rails.

The Bells and Whistles

The Rails Asset Pipeline is, in a word, awesome. If you’re not familiar with it, I highly encourage you to read the Asset Pipeline Rails Guide. You are likely familiar with the Asset Pipeline, at least in terms of serving your JavaScripts and stylesheets. As it turns out, HTML templates aren’t so different. When it comes to HTML templates, the biggest advantage of integrating client-side templates with the Asset Pipeline is the file name fingerprinting and far-future cache expiration that it allows.

Another benefit of using the Asset Pipeline is that, during asset pre-compilation, Rails will also generate a gzipped version of the compiled asset. This is a great way to reduce the size of your template requests, but one that involves more in-depth web server configuration, so we won’t go into detail on that optimization here. But worry not, your web server likely does some kind of compression already, so the benefits of gzipped templates would be less pronounced anyway.

Two other benefits of using the Asset Pipeline worth mentioning are preprocessors and minification. Preprocessors are templating engines that simplify your template code. Most Rubyists are familiar with the ERb preprocessor, commonly used by Rails view templates, which allows you to execute Ruby logic during the compilation of a template. Another preprocessor for HTML markup is Haml, which offers a more minimal alternative markup language that compiles to standard HTML.

Minification is the process of reducing the size of source code without changing the functionality of that code. The benefits of minification are impressive when it comes to JavaScript and CSS however less pronounced when it comes to HTML. If your HTML contains a lot of white-space, minification may have a noticeable impact on template size. Otherwise, minification of templates likely won’t offer a significant improvement because typical compression strategies handle normal amounts of white-space pretty well.

Now, let’s go back and take a closer look at fingerprinting and far-future cache expiration.

During the pre-compilation of assets, Rails generates a checksum of a compiled file and appends that checksum hash to the name of the file as a fingerprint. So, for example, index.html becomes something more like index-a029bd03bea21da7d02c0e9d272edc3a.html after compilation. The real beauty of fingerprinting is that it links a file’s name to its content. More succinctly, this means when a file’s content changes, so too does the name of the file.

With asset file names fingerprinted in this way, you can configure your web server to serve assets with an Expires header that will tell web browsers not to check for an updated version of the file for as much as a year (the RFC recommended maximum).

This is possible because anytime a file changes, the fingerprint on the file name will also change. This will cause the browser to treat the asset like a new file. In brief, this means that if a file doesn’t change, any user who has already downloaded the file won’t try to download it again for a year. If the file does change however, the file name will also change and the browser will automatically and transparently download the new version, which will again be cached for up to a year.

This is a pretty awesome enhancement that can have a noticeable impact on the number of requests served and the page-load time for repeat visitors.

Part I Takeaways

If you’re already excited about the prospect of moving your AngularJS templates over to the Asset Pipeline, great! If not, it may be because nothing we’ve covered so far is all that new of an idea. In fact, the topic has been covered by a number of blogs, some of which you can find in the Additional Resources section at the end of this article.

So why the rehash? Two reasons. First, there doesn’t seem to be a clear consensus in the community that the Asset Pipeline is the right way to handle client-side templates. Second, even those that advocate for using the Asset Pipeline tend to leave it at that without digging into the implications that come with using the Asset Pipeline to server HTML templates. Hopefully this post has addressed the first point, setting us up to take a deeper dive into the latter reason in Part 2.

In the meantime, if I’ve missed something, you disagree, you want a better example, or you have a question, drop me a note in the comments, I’d love to hear about it.

Additional Resources

http://guides.rubyonrails.org/asset_pipeline.html
http://guides.rubyonrails.org/asset_pipeline.html#far-future-expires-header
http://www.amberbit.com/blog/2014/1/20/angularjs-templates-in-ruby-on-rails-assets-pipeline/
http://gaslight.co/blog/4-lessons-learned-doing-angular-on-rails
https://groups.google.com/forum/#!topic/angular/V3hMGIDwE3o
http://cmme.org/tdumitrescu/blog/2013/12/rails-backbone-templates/
http://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html
https://github.com/sstephenson/sprockets#javascript-templating-with-ejs-and-eco