To get started with JS templates, there are two things you need to agree to, as a default position (down the road, optimization might take you to a different conclusion). First:
That’s easy. It’s so easy I’m not even going to explain it. To do otherwise is difficult to maintain and fraught with ridiculous problems like extremely long strings and incorrectly escaped apostrophes. Second:
It is bad to send data from your server as HTML instead of JSON.
If you don’t want your pages to be dynamic, that’s perfectly fine. So fine, in fact, that you’re excused from reading the rest of this blog post and can go back to whittling yourself a neckbeard trimmer. If you do, though, sending HTML in response to an XHR is silly. What are you going to do with that? Nothing, Einstein. Nothing is what you’re going to do with it. You can insert it into the page once and after that it’s just more junk. If you’re not going to send data back from the server you might as well have static pages.
Still reading? Ok, great. So we have established that we need templates, because the alternative we’re left with is lots of DOM manipulation and nobody wants that. Let’s talk how these things get used.
replacing sections of a page
This is the most basic component. You get some data. You have a template. You hydrate said template. You attach the result to the DOM. And you need to think no more deeply about it than that.. if all you’re making is a proof of concept. For performance, we should probably add a requirement that these templates be precompiled, and that the resulting functions be cached somewhere. For flexibility, we probably want these templates to be composable, so that we don’t have to re-render more of the page than is affected by whatever data we’ve received. So add an additional requirement for partials. If we want don’t want to present all the data exactly as it’s received, we need to be able to control the scope the template function executes in, so that we have access to utilities that transform raw numbers and strings into things like currency and proper names. How we do that depends on the template engine we choose, but we need to be able to do it somehow.
How templates get loaded is an elephant that reveals itself very quickly in whatever room you happen to be designing your application in. Most template engines will accept any string as input, meaning the templates themselves can exist anywhere. Some people think that means they should be chunks of HTML in your normal, server-rendered page. I think that’s foolhardy and inflexible. Templates should go in their own files. In some engines, partials can be defined inline, and in certain scenarios that makes sense – for example, if you have a module that needs to be re-rendered frequently, and some subsection of it will be re-rendered alone only if some error occurs. This assumes, though, that the error state partial isn’t useful anywhere else in the app. Otherwise it should be its own file.
A lot of us start experimenting with templates naïvely lazy-loading them with XHRs. If you begin to break your templates up intelligently, though, that quickly turns into a lot of HTTP requests. Templates need to be packaged up and delivered the same way any resources do, minimizing requests while maximizing the number of manageable chunks the client has the opportunity to cache. You don’t want to concatenate all your templates into one long JS file as variables, though – then you just have the same old line break and character escaping headaches again. You want to concatenate the compiled functions they produce. Those functions can then end up in the same nice page-specific concatenated JS file you were going to send down to the client anyway.
While we’re talking about resource loading, it’s a good time to mention that a lot of template engines are output agnostic and will build any kind of code you want, including CSS. So if you’re making changes that might also require loading a lot of CSS you might not need, or that will break your page under the wrong circumstances, you can put that in a template, too, if you like. This might be nice if you want to A/B test some changes based on a factor you’ll only know in a certain client-side state, for instance. Your CSS can be a template function you feed your parameters into to output the correct version without needing to change classes in the DOM, you can attach the resultant CSS to the DOM, and you’re done.
templates outside of SPAs
Mostly we talk about templates for single-page apps, because a good deal of rendering needs to occur there, but that’s not the only place they’re useful. There are one-off areas on a lot of otherwise static pages that might have cause to re-render outside of any formal concept of an app. And then there are the static pages themselves. As soon as a page needs to be rendered once on the server and then another n times on the client, we have a case for shared templates. Before you say it, no, this doesn’t automatically mean you have to use Node, and it doesn’t mean you automatically have to use Mustache. Use whatever the hell you were going to use anyway. If there’s no parser for that template engine with that server-side language, write a parser. Or, you know, use Node or Mustache. But remember that this is computer programming and there’s always a way to do it, it’s just a matter of writing more code.
If you’re doing this server-side, presumably whatever parser you’re using already hooks into your routing and controllers. So that’s awesome. If you’re doing it client-side and using an application framework, the pattern seems to be that either rendering is completely up to you and it’s a piece of cake, or your framework is so tightly tied to your markup that we’re not even having this conversation. If you’re not using a client-side framework you will probably need to write something. The nice thing is that you concatenated all your templates as compiled functions and passed them all in when the page loaded, or required them for the section of code where they’re needed, so all you have to do is pass them data and put the result in the right place. This is where it becomes handy to have all your templates and partials on the same object. Some engines do this already, some will need to be managed by you. This is so you can write a nice abstraction that accepts the name of the template to render, its data, and its container, and you don’t have to worry about which partials it’ll be using. Some engines also need any helper functions used within them passed in each time they’re rendered, and that’s another thing you probably want to keep separate from your main code.
widgets and plugins
There’s an inherent annoyance in creating utilities for distribution (even if it’s just among various pieces of your own app) that need to manage their own HTML and CSS. You want the person using the widget to be able to implement it with no more than an initialization call and some small set of options. You don’t want them to have to import the JS, HTML, and CSS on every page where it’s used, particularly if those pages might be built up from other server-side templates and the place the widget is used might be a partial within that system. At minimum, it’s an improvement to only have to import the JS, and have it take care of the rest (even better would be to use AMD or something to load the widget).
Putting your HTML and CSS in templates makes distribution easier, cause you have something with (presumably) a different file extension that can live in the same directory as the script that will load it and not look like it’s been misorganized. Better still, the minified version of the widget can contain the compiled templates as functions, so its users only need a single file. If you offer an option to let users supply their own path to a template, you can make your templates overridable defaults, though you probably want to set some ground rules about what template engine is allowed and how templates will be loaded and cached (e.g., are you expecting to use require.js).
mixing template engines
I’m going to be straight with you people: we have too many template engines and that’s a problem because of everything above. Templates themselves help us modularize our HTML and whatever else in the same way we can our JS, but using a bunch of different types of templates makes things messier because we then have modules we can’t share. If we want to combine the modules for use in some implementation, we have to load all the template engines needed. And that’s no big deal, the modules can take care of doing that, provided it’s not via some naïve mechanism that will add a script tag to the page potentially loading the same engine again and again. Except that to make that provision we have to assume they’re using a sophisticated loader, which is also a dependency.
That’s everything I could think of. If I missed something, let me know!