External Template in Underscore

External template in Underscore

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.

Way to externalize underscore templates

I ran into the same issue. I found an example online, I modified it to make it work for what I was doing it for (they were pulling html files and I wanted to pull ASP.net pages) but I can't find the example online again to give that person the credit. However, here is my modified code.

The first is the template loader:

if (!window.JackTemplateLoader) {

function JackTemplateLoader(params) {
if (typeof params === 'undefined') params = {};
var TEMPLATE_DIR = params.dir || '';

var file_cache = {};

function get_filename(name) {
if (name.indexOf('-') > -1) name = name.substring(0, name.indexOf('-'));
return TEMPLATE_DIR + name;
}

this.get_template = function (name) {
var template;
var file = get_filename(name);
var file_content;
var result;
if (!(file_content = file_cache[name])) {
$.ajax({
url: file,
async: false,
success: function (data) {
file_content = data;
file_cache[name] = file_content;
}
});
}
return file_content;
}

this.clear_cache = function () {
template_cache = {};
};

}
}

Then inside my Marionette App, I created an addInitilizer that over writes Marionettes template loader.

    app.addInitializer(function (options) {
app.JackTemplateLoader = new JackTemplateLoader({ dir: "/api/ApplicationScreens/", ext: '' });
Backbone.Marionette.TemplateCache.prototype.loadTemplate = function (name) {
if (name == undefined) {
return "";
} else {
var template = app.JackTemplateLoader.get_template(name);
return template;
}
};

The best part is the template loader will cache my templates so they are only loaded once. I am working on making a change to this code to send a flag that will indicate whether or not I want to cache the template.

How to bind between the underscore template and other html files?

Remove the <script> tags from list.html, they aren't necessary when loading from an external file, the HTML file should look like this:

<div id="columns">
<% _.each(result, function(result){ %>
<div id="<% result.get('id') %>" class="content">
<a href="<% result.get('url') %>">
<figure>
<img src="<% result.get('imgSrc') %>">
<figcaption><% result.get('title') %></figcaption>
</figure>
</div>
<% }); %>
</div>

underscore.js nested templates

You can pass the nested template as a variable in the template assignments to the main template, e.g.:

HTML:

<script type="text/template" id="sub_template">
<article>
<h1>id: <%= id %><h1>
</article>
</script>

<script type="text/template" id="main_template">
<% for (var i = 0; i < num; i++) { %>
<%= renderSub({id:i}) %>
<% } %>
</script>

JS:

 var renderSub = _.template( $('#sub_template').remove().text() ),
renderMain = _.template( $('#main_template').remove().text() );

renderMain({num:5, renderSub:renderSub});

external html template for underscore.js and backbone.js

Got this from http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/#comment-35324

Create a separate js file for this and call it before your js files for model,collection and views.

tpl = {

// Hash of preloaded templates for the app
templates:{},

// Recursively pre-load all the templates for the app.
// This implementation should be changed in a production environment. All the template files should be
// concatenated in a single file.
loadTemplates:function (names, callback) {

var that = this;

var loadTemplate = function (index) {
var name = names[index];
//console.log('Loading template: ' + name);
$.get('templates/' + name + '.html', function (data) {
that.templates[name] = data;
index++;
if (index < names.length) {
loadTemplate(index);
} else {
callback();
}
});
}

loadTemplate(0);
},

// Get template by name from hash of preloaded templates
get:function (name) {
return this.templates[name];
}

};

After that add this to your router

tpl.loadTemplates(['filename-of-your-external-html-file'], function () {
app = new AppRouter();
Backbone.history.start();
});

That should do it. But again not recommended for production environment as there will be hundreds to get request and may cripple your application.

Load external underscore template

At the moment you call $('#tpl_observation_summary').html(), the DOM seems to not be ready and you get null as the first argument of _.template, thus resulting in underscoreJS not being able to work with a string which is not one.

Then the bootstrap method cannot be defined and so cannot be called as a function of $.app.

Template initialization can be included into the first initialize call of your Backbone views.

Calling a function inside underscore template defined in Backbone view

_.extend doesn't work like that, it needs 2 or more objects, and the keys will be merged. It looks like you probably took that snippet from this other question, but it's incorrect and/or outdated.

extend _.extend(destination, *sources)

Shallowly copy all of the properties in the source objects over to
the destination object, and return the destination object. Any
nested objects or arrays will be copied by reference, not duplicated.
It's in-order, so the last source will override properties of the same
name in previous arguments.

_.extend({name: 'moe'}, {age: 50});
=> {name: 'moe', age: 50}

This would work:

_.extend({ output: output }, { testFunction: this.testFunction });

But a better way in this simple case would be to avoid _.extend altogether.

this.$el.html(this.template({
output: output,
testFunction: this.testFunction
}));

In a real life situation, you may want to use the view context (this) within the function. To do this, you would need to use .bind(this) on the function when passing it to the template.

this.$el.html(this.template({
output: output,
testFunction: this.testFunction.bind(this)
}));

Underscore Templates: passing variables from a parent template to a child template

When you compile an Underscore template, Underscore turns your template inside out and builds a function that looks, more or less, like this:

function(obj) {
// Some set up...
with(obj || {}) {
// The template as JavaScript...
}
return theTemplateText;
}

You can't depend on the parameter being called obj, that's bound to break soon or later. You should have safe access to the arguments object though. Having arguments allows you to call other functions with exactly the same arguments as the current function without having to know what the arguments are, you just use apply.

If you have listHeaderTemplate and listFooterTemplate available to your template, you can simply say:

<script type="text/template" id="list-template">
<%= listHeaderTemplate.apply(this, arguments) %>
stuff that
displays a list
<%= listFooterTemplate.apply(this, arguments) %>
</script>

An easy way is to pass those functions as arguments to your template:

var listTemplate       = _.template($("#list-template").html());
var listHeaderTemplate = _.template($("#list-header-template").html());
var listFooterTemplate = _.template($("#list-footer-template").html());

var html = listTemplate({
foo: 11,
listHeaderTemplate: listHeaderTemplate,
listFooterTemplate: listFooterTemplate
});

Demo: http://jsfiddle.net/ambiguous/vwjm1kta/

Underscore template not working

You need to add the html resulting to DOM, create a target element:

<div id="target"></div>

and append the resultinghtml

...
var resultinghtml = usertemplate({users: data.users});
$('#target').html(resultinghtml);
...


Related Topics



Leave a reply



Submit