Precompiled Handlebar Inline Templates With Backbone - javascript

I know how to precompile my Handlebar templates when I externalize them by having them in a separate file. Lately, I have been shortening my templates and using them inline. For example I have the following Backbone View.
var ChosenVehicle = Backbone.View.extend({
className: "car selection",
initialize: function(options) {
this.data = options.chosenData;
},
template: Handlebars.compile(
'<h4 class="number">01</h4>' +
'<img src="assets/img/cars/{{vehicleSlug}}.jpg" alt="{{vehicle}}">' +
'<p class="flush">' +
'<small class="text--center caps">' +
'{{vehicle}}' +
'</small>' +
'</p>'
),
render: function() {
var chosenCar = _.find(this.model.get("cars"), function(car) {
return car.id.toString() === this.data.vehicleId;
}, this);
this.$el.html(this.template(chosenCar));
return this;
}
});
What are the performance ramifications using the above and not precompiling the templates? I prefer to have my templates setup above as I find it much easier to read and maintain. Now obviously I am unable to precompile my template, or is there another solution?
Thanks
Tyrone

Well, you may think this is easier to read and maintain, but it's not. You don't want to open a javascript file to edit your templates/markup; just as you don't want to open CSS to debug Javascript (remember expression()?).
The idea is to precompile your template through your build system. This can be easy if you load your template via a requireJs loader plugin; e.g.:
template: require("hbs!templates/listItem")
You can also checkout how Backbone Boilerplate does it right now: https://github.com/tbranyen/backbone-boilerplate (Although, we spoke about going the requireJs plugin road in the near future)
Checkout Github-Viewer for a working example of Backbone Boilerplate templates loading/precompiling: https://github.com/tbranyen/github-viewer

Related

load all templates at once

The following code loads templates.
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('tpl/' + 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];
}
};
and u can load templates like
tpl.loadTemplates(['header', 'wine-details', 'wine-list-item'], function() {
});
But here the templates are defined in seperate html file.
Is it possible all my templates are defined in one html file and i load them all together.
Do i need to write some kind of parser. The reason is for my home page i have 40 templates and I dont want to create 40 Html template pages instead it would be i can keep them in one single html page and than load them all together.
Is this possible?
Yes you don't need to be doing it like this.
Check this: https://github.com/requirejs/text
define([
'some/module',
'text!some/module.html',
'text!some/module.css'
], function (SomeModule, HtmlFile, CssFile) {
'use strict';
// ....
}
This can load text resources. When you are ready to deploy your app you can compile all your js files and templates into a single minified file using r.js. You can do this via command line, or as a grunt or gulp task.
There are many template libraries which you can find requirejs plugins for (often extended off that text plugin ^^). For example this one for Handlebars https://github.com/jfparadis/requirejs-handlebars or this one for underscore https://github.com/jfparadis/requirejs-tpl. Just google search "requirejs «template engine name»".
If you want to have multiple templates in one html file you need a way to separate them. The nicest way to do this is using the new template tag.
You can, define templates single file, under different container with Ids. load single html the way you are loading here, create a jquery node with template html, run through children read id attribute and save html of each child saved as that.templates[id_you_read] = child.html();
But this is not the right way of doing stuff. use requirejs with text plugin as in the prev answer.

Force Backbone or Underscore to always escape all variables?

I'm new to Backbone, and am helping maintain an app. I'd like to make the default in all normal situations for Backbone to be escaping model data, to help avoid XSS attacks by default.
I know we can do this using
<%- someModelAttribute %>
and
model.escape('attr')
to escape data in our app, but I'd like to switch it so
<%= someModelAttribute %>
and
model.get('attr')
did the same as well.... So by default, all existing code and future code that uses these tags and methods is escaped by default. Then I'd like to introduce another model method like "model.getDataThatShouldBeSafeHtml" to make it 100% clear to developers when they're getting data that should include HTML.
So is there some way for me to switch the "<%=" tag and the "model.get" methods to be the same as their escape equivalents?
I only ask as I thought this might've been done somewhere before, or already be part of backbone, and I want to avoid rebuilding the wheel!
I found an easy way to make it so all templates by default are escaped. Since Backbone uses Underscore for it's templating engine, I googled around and found that you can customize the underscore delimiters using _.templateSettings, described here on the Underscore site. Note that if you make sure all of your html is written out using these templates, then you're covered for all XSS. So don't skip using templates in some simple scenarios, use them to avoid XSS in those cases as well!
You can test it out using this fiddle: http://jsfiddle.net/vx0pw2n0/
So all I did was make it so both <%= and <%- are used to display escaped data by default. The evaluate tag <% still remains, and can be used to output whatever HTML you want using the print statement. I also introduced a new tag, <%cleanHtml that can be used to output HTML without it being escaped, and without needing to say print(someVariable)
<script type="text/javascript">
//
// This is the important part - The part that changes what underscore uses
// for template delimiters.
//
_.templateSettings =
{
escape: /<%[=-]([\s\S]+?)%>/g,
interpolate: /<%cleanHtml([\s\S]+?)cleanHtml%>/g,
evaluate: /<%([\s\S]+?)%>/g
};
// Test it out
var t = _.template($('#t').html());
var html = t({ title: '<b>pancakes</b>' });
$("#target").html(html);
console.log(html);
</script>
<!-- Sample Underscore Template showing different ways of using it -->
<script id="t" type="text/x-underscore">
<div><%= title %></div>
<div><%- title %></div>
<div><%safeHtmlOnly title safeHtmlOnly%></div>
<div><% print(title) %></div>
</script>
<div id="target"></div>
Making this work globally
To get this to work globally, you may have to configure Underscore as a module, which can be done like so:
Implement as a dependency on Backbone
Configure Require.js modules
// When you initially setup require.js, add a new module to configure underscore
// Make it a dependency of backbone, so it'll always be loaded whenever
// backbone is used.
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscoreConfig', 'underscore', 'jquery'],
exports: 'Backbone'
},
jquery: {
exports: 'jQuery'
}
}
});
underscoreConfig.js
define(['underscore'], function (_) {
'use strict';
_.templateSettings =
{
escape: /<%[=-]([\s\S]+?)%>/g,
interpolate: /<%cleanHtml([\s\S]+?)cleanHtml%>/g,
evaluate: /<%([\s\S]+?)%>/g
};
return _;
});
You could extend Backbone.Model to create a reusable base class that does this for you. Something like this (untested):
BaseModel = Backbone.Model.extend({
getSafeAttributes: function() {
var safe = {};
_.each(this.attributes, function(key, value) {
safe[key] = _.escape(value);
});
return safe;
}
});
I'm guessing your render functions look something like
this.$el.html(this.template(this.model.attributes));
So, instead of that, you'd write:
this.$el.html(this.template(this.model.getSafeAttributes()));
and just make sure your models extend the base class instead of Backbone.Model.
It's perfectly acceptable to modify backbone.js and underscore.js to achieve a similar outcome, but it does make it a pain to upgrade, which is why I'd go with the base class instead.
As far as I know backbone depends on the template engine provided by underscore.
How to use underscore.js as a template engine?
By underscore you can change the regex patterns to always escape, but it won't call the escape method of your model, so you have to create your own template engine to do that...
For example you can fork the templating code from underscore and develop it in your own way and use that instead of _.template().

Templating in a Javascript-Only Application

I will be developing a large, AMD-based javascript application, structured with backbone.js and potentially require.js. I am doing research on the best way to go, and one thing I would like to get into using is a template library, particularly handlebars.js.
My problem here is that I want to make javascript-only modules that can be loaded and implemented on the fly, well after the application is loaded. Templates are based on HTML tags, but I don't want to include html pages after the fact.
My question is: Is it stupid, or valid practice to mock up HTML templates as strings in your javascript, and then render them? I feel like it would kill the entire point in performance alone, but I really don't know.
This is an example of what I am talking about:
var render = function(html, model) {
var tmpl = Handlebars.compile(html);
return tmpl(model);
}
$(document).ready(function() {
var template = '<div class="entry">' +
'<h1>{{title}}</h1>' +
'<div class="body">{{body}}</div>' +
'</div>';
var model = {
title: 'I love templating,',
body: 'And so do you!'
}
template = render(template, model);
$(document.body).append(template);
});
Is this terrible practice, or is there a better way to implement this in a javascript-only application?
Template are used to separate html from javascript code.
I suggest you to look at requireJS text plugin to load your template code in an AMD environment.

Backbone.js doesn't work while Underscore, JSON, and jQuery do

I can't get the most basic functions of Backbone.js to work on my web application.
JQuery, JSON, and Underscore.js do work, but for some reason Backbone.js doesn't.
I know this is a very simple problem, but scouring the Internet has not helped thus far.
I would really appreciate any help!
Details of app:
Framework: Rails 3.2.6
All JS files are stored under app/assets/javascripts
JS files present: index.js, application.js, backbone.js, json2.js, underscore.js
How I'm testing which libraries work
I've put tests to see what works in the index.js file, which contains the following:
$(function(){
//test that this Javascript file is working:
alert('index.js javascript file works');
//test that jQuery is working:
var jQuery_test = $("#app").attr('id');
alert("jQuery works? (following should be 'app'): " + jQuery_test);
//test that JSON is working
var json_obj = {"key1":"value1", "key2":"value2", "key3":"value3"};
var json_string = JSON.stringify(json_obj);
alert('JSON library works? (following should be a JSON string): ' + json_string);
//test that Underscore is working
var array1 = ["one", "two", "three"];
var x = _.first(array1);
alert("Underscore library works? (following should be 'one'): " + x);
//test that Backbone is working
var ItemView = Backbone.View.extend({
tagName: 'li'
});
var item = new ItemView();
alert("Backbone library works? (following should be '[Object HTMLLIElement]'): " + item.el);
});
The tests above pass for all but Backbone.
Perhaps I'm using a bad test?
Really have no idea what could be wrong.
I dont think it has to do with how I'm loading the files, because I'm loading Backbone the same way I'm loading the other libraries, and they all work.
Thanks for any help!
Underscore is a hard dependency for Backbone and should be loaded before backbone.js, as any other dependency you may have (jQuery/Zepto).

HTML templates inside JavaScript file... What am I doing wrong?

What I'm trying to achieve is to store HTML templates for everything that needs to be generated on the fly inside a separate js file (instead of rendering it in the page).
The buildHtml function how its currently setup works fine. Where I'm stuck is what if.. I've another variable inside the template object say 'input3' and its markup is something like <div>(exact markup from commonInput)</div>
I tried using it as input 3 : '<div>' + this.commonInput + '</div>' but it turns out you cannot refer object properties from inside using this (explained here).
I could create 'input3' with full html but for big html chunks this approach is not very useful.
Looking for
solution to this specific problem
reasons if this approach is a bad idea
better alternatives
$j(document).ready(function() {
var namespace = window.namespace || {};
namespace.feature = namespace.appName || {};
namespace.feature.templates = {
input1 : '<div>'+
'<p class="abc">Hey {username}</p>'+
'</div>',
input2 : '<div>'+
'<div class="description">{description}</div>'+
'</div>',
commonInput : '<div class="common">Common code</div>'
};
namespace.feature.module = function() {
var container = $j('#container'),
t = namespace.feature.templates;
var buildHtml = function(type) {
var tmpHtml = t.input1 + t.commonInput + t.input2;
container.append(tmpHtml);
}
var init = function() {
buildHtml();
}
return {
init : init,
};
}();
namespace.feature.module.init();
});
just on the top of my head.
You could write the templates as functions that build strings.
input3 : function(param) {
return '<div>' + param + '</div>'
}
then:
var tmpHTML = t.input1 + t.input2 + t.input3(t.commonInput);
Also, i like to build my own DOM objects when building HTML. and avoid the use of hardcoded strings.
http://mg.to/2006/02/27/easy-dom-creation-for-jquery-and-prototype
I'm not sure what your exact question is, but as far as better alternatives go,
I would suggest using jQuery templates.
Here is a benchmarking page for all the different templating engines and their performance:http://jsperf.com/dom-vs-innerhtml-based-templating
You can look at the revisions to find different combinations of engines as well as run them on different browsers to see speed differences.
Update: The original jquery template project is no longer active. This is the new home for the old project: https://github.com/BorisMoore/jquery-tmpl
I would no longer recommend jquery-templates since there are much better alternatives now. The last time I checked, dustJs by linkedIn fork seems to be the most promising one.

Categories

Resources