External template in Underscore

I use Underscore template. It is possible to attach a external file as template?

In Backbone View I have:

 textTemplate: _.template( $('#practice-text-template').html() ),

 initialize: function(){                                            
  this.words = new WordList;            
  this.index = 0;
  this.render();
 },

In my html is:

<script id="practice-text-template" type="text/template">
   <h3>something code</h3>
</script>

It works well. But I need external template. I try:

<script id="practice-text-template" type="text/template" src="templates/tmp.js">

or

textTemplate: _.template( $('#practice-text-template').load('templates/tmp.js') ),

or

$('#practice-text-template').load('templates/tmp.js', function(data){ this.textTemplate = _.template( data ) })

but it did not work.


Here is a simple solution:

var rendered_html = render('mytemplate', {});

function render(tmpl_name, tmpl_data) {
    if ( !render.tmpl_cache ) { 
        render.tmpl_cache = {};
    }

    if ( ! render.tmpl_cache[tmpl_name] ) {
        var tmpl_dir = '/static/templates';
        var tmpl_url = tmpl_dir + '/' + tmpl_name + '.html';

        var tmpl_string;
        $.ajax({
            url: tmpl_url,
            method: 'GET',
            dataType: 'html', //** Must add 
            async: false,
            success: function(data) {
                tmpl_string = data;
            }
        });

        render.tmpl_cache[tmpl_name] = _.template(tmpl_string);
    }

    return render.tmpl_cache[tmpl_name](tmpl_data);
}

Using "async: false" here is not a bad way because in any case you must wait until template will be loaded.

So, "render" function

  1. allows you to store each template in separate html file in static dir
  2. is very lightweight
  3. compiles and caches templates
  4. abstracts template loading logic. For example, in future you can use preloaded and precompiled templates.
  5. is easy to use

[I am editing the answer instead of leaving a comment because I believe this to be important.]

if templates are not showing up in native app, and you see HIERARCHY_REQUEST_ERROR: DOM Exception 3, look at answer by Dave Robinson to What exactly can cause an "HIERARCHY_REQUEST_ERR: DOM Exception 3"-Error?.

Basically, you must add

dataType: 'html'

to the $.ajax request.


EDIT: This answer is old and outdated. I'd delete it, but it is the "accepted" answer. I'll inject my opinion instead.

I wouldn't advocate doing this anymore. Instead, I would separate all templates into individual HTML files. Some would suggest loading these asynchronously (Require.js or a template cache of sorts). That works well on small projects but on large projects with lots of templates, you find yourself making a ton of small async requests on page load which I really dislike. (ugh... ok, you can get around it with Require.js by pre-compiling your initial dependencies with r.js, but for templates, this still feels wrong to me)

I like using a grunt task (grunt-contrib-jst) to compile all of the HTML templates into a single templates.js file and include that. You get the best of all worlds IMO... templates live in a file, compilation of said templates happen at build time (not runtime), and you don't have one hundred tiny async requests when the page starts up.

Everything below is junk

For me, I prefer the simplicity of including a JS file with my template. So, I might create a file called view_template.js which includes the template as a variable:

app.templates.view = " \
    <h3>something code</h3> \
";

Then, it is as simple as including the script file like a normal one and then using it in your view:

template: _.template(app.templates.view)

Taking it a step further, I actually use coffeescript, so my code actually looks more like this and avoid the end-of-line escape characters:

app.templates.view = '''
    <h3>something code</h3>
'''

Using this approach avoids brining in require.js where it really isn't necessary.