handlebar compilation errors - javascript

Hi I have been trying to use backbonejs and handlebar templates but seems like either my jSON is wrong or data is not properly parse . Getting
Uncaught Error: You must pass a string to Handlebars.compile. You passed undefined
Code can be found at
jsfiddle
any advice will be appreciated

Your fiddle is broken in various ways but the odds are that you're doing this:
template: Handlebars.compile($('#tpl-page-list-item').html()),
before there is a #tpl-page-list-item available. This will happen if your page looks like:
<script src="your_backbone_javascript.js"></script>
<script id="tpl-page-list-item" ...></script>
as your Backbone view will be parsed before <script id="tpl-page-list-item"> gets added to the DOM. You can wrap your Backbone views in a document-ready handler (with proper namespacing to account for the function wrapper):
$(function() {
window.PageListItemView = Backbone.View.extend({
template: Handlebars.compile($('#tpl-page-list-item').html()),
//...
});
});
or compile the template when instantiating your view:
initialize: function() {
// Or check if it is in the prototype and put the compiled template
// in the prototype if you're using the view multiple times...
this.template = Handlebars.compile($('#tpl-page-list-item').html());
//...
}

Related

Reducing code duplication in Ractive.js

I'm running into the issue that many different pages on my website (created using Ractive.js) need the same functions, mostly functions that perform AJAX calls back to the server. I would normally store all these functions in one JS file and include it in all of my pages for common functionality, but when I do this the function aren't recognized inside my Ractive code for the individual pages.
So it looks something like this
<script src="src/to/common/library"></script>
<div id="target"></div>
<div id="template">
// Ractive code
load_models(); // defined in common library, throws error that it's not defined.
</div>
I figured it out. Instead of doing
$(function() {
var MAIN = new Ractive({
el: '#target',
template: '#template',
I can just do
$.getScript("/app_name/static/js/scripts/common.js", function() {
var MAIN = new Ractive({
...
)};
// all functions defined in common.js are available for your use now
});

Backbone template loading using promises

Similar to this question, I'm looking for a way to load templates via Javascript promises. The issue I'm running into is returning the compiled template to the Backbone View. Note: this.getFile() returns a new Promise, which fetches the template as expected.
template: function(attributes) {
this.getFile('/path/to/template').then(function(tpl) {
var compiled = Handlebars.compile(tpl);
return compiled(attributes);
}, function(error) {
console.error('Failed!', error);
});
}
Within the View's initialize, I have a listener set to
initialize: function() {
this.model.on('change', this.render, this);
}
which triggers as expected when data is fetched from the server.
The problem is when the render function executes the line
render: function() {
...
this.$el.html(this.template(attributes));
...
}
nothing renders to the html as I would expect. I'm sure I'm missing something simple within the template promise callback, but no matter what I change, the html does not render and no errors appear.
Like #muistooshort have pointed out. The template function does not return anything, it simply does an async call to another function and deal with it.
However, your render function is syncronous, it execute template function, then add whatever is returned directly to the $el. Which in this case, there is nothing to add.
There are two solutions to this problem.
1. Make the template function to return a promise. And in render, wait for the template to finish before adding stuff to $el.
Example:
template: function(attributes) {
return this.getFile('/path/to/template').then(function(tpl) {
var compiled = Handlebars.compile(tpl);
return compiled(attributes);
});
}
Note this return this.getFile..., will return the promise, then will resolve with the value compiled(attributes).
Then your render function could be:
render: function() {
var self=this;
this.template(attributes).then(function(data){
self.$el.html(data);// data is the stuff that was compiled.
})
}
However, may I suggest a different solution as in how you manage your templates?
As a personal opinion, the templates, either ejs, jade, or hbs, are actually very small when compiled. So these data could be compiled in your js files using require. What I do is I use a JStemplate file
module.exports={
t1:require(path/to/template1),
t2:require(path/to/template2),
}
Then, in other backbone view js files. I could do
var allTemplates=require('./JStemplate.js');
...
render:function(){
this.$el.html(allTemplates.t1(attributes));
}
This in my opinion is easier to handle, and faster in client side, because you don't have to get the file in client.

Template two models in one view - Backbone/Marionette

I'm trying to use two models in one view, and template using both of them. I'm working with Marionette. Here is me initialization of the view:
main_app_layout.header.show(new APP.Views.HeaderView({
model: oneModel,
model2 : twoModel}
));
Here is my view:
APP.Views.HeaderView = Backbone.Marionette.ItemView.extend({
template : '#view_template',
className: 'container',
initialize: function() {
//This correctly logs the second model
console.log(this.options.model2);
}
});
And here is the template:
<script id="view_template" type="text/template">
<p>{{twoModel_label}} {{oneModel_data}}</p>
<p>{{twoModel_label2}} {{oneModel_data2}}</p>
</script>
It renders everything correctly using the oneModel data, but doesn't render the second, even though it logs it correctly. I'm using Mustache as my templating language.
Can anyone help?
You can override the serializeData method on your view and have it return both models' data:
APP.Views.HeaderView = Backbone.Marionette.ItemView.extend({
// ...
serializeData: function(){
return {
model1: this.model.toJSON(),
model2: this.options.model2.toJSON()
};
}
});
Then your template would look like this:
<script id="view_template" type="text/template">
<p>{{model1.label}} {{model1.data}}</p>
<p>{{model2.label}} {{model2.data}}</p>
</script>
try creating a complex model that contains the two models, this new model will have the other two as properties and you can acccess the properties of each one like this answer explains..
Access Nested Backbone Model Attributes from Mustache Template

External templates and "el" with Knockout and RequireJS?

I am trying to use Knockout ViewModels as self-contained "widgets" that can be placed into any (or multiple) DOM nodes on the page. I had an approach in Backbone that seemed to work well and I'm trying to convert the concept to Knockout.
In Backbone view I would do something like this, using the RequireJS text plugin to pull the template and inject it into the el:
define(['text!templates/myTemplate.html',], function(templateHTML){
var view = Backbone.View.extend({
initialize:function() {
// yes I know the underscore templating stuff doesn't apply in Knockout
this.template = _.template( templateHTML );
this.render();
},
render:function( ) {
// the $el is provided by external code. See next snippet
this.$el.append(this.template(myData));
return this;
}
// other view behavior here
});
return view;
});
And then some other piece of external code could place that view into an existing DOM node:
new MyBackboneView({el: $('#myExistingDivID')});
In Knockout, the closest I can find to this approach is to have the external code use the Text plugin to pull the template, inject it into the div, and then apply the KO bindings:
var mydiv = $('#myExistingDivID');
mydiv.html(myTemplateHTML);
ko.applyBindings(new MyKOViewModel(), mydiv[0]);
1 - Is there a recommended way in Knockout to have the ViewModel itself inject the external template HTML based on the equivalent of Backbone's "el" concept? The key is that the external (router-ish) code controls where the content will be placed, but the ViewModel controls the actual details of the content and where to get the template.
2 - If yes, should I take this approach, or am I abusing the way that Knockout and MVVM are intended to be used?
You can override the default template source and then use that with the default render engine like
var stringTemplateEngine = new ko.nativeTemplateEngine();
stringTemplateEngine.makeTemplateSource = function (template) {
return new StringTemplateSource(template);
};
ko.setTemplateEngine(stringTemplateEngine);
Quick example I did
http://jsfiddle.net/3CQGT/

what does Compiling a template in backbonejs mean and little more

I was trying out Backbonejs read through couple of docs. It is an MVC
framework for client side. Inside the views we compile a template and
render them(attach them to DOM, or do some manipulation). Backbone has
a dependency of underscore js, which is the templating stuff.
With Bbone, when manipulating views el comes into picture. My
understanding of el is that it references the DOM Object. If no
dom object is assigned to it, it assumes an empty div.
var choose_view = new SearchView({ el: $("#choose_me") });
So in the above case, the div with id choose_me will be manipulated,
when the choose_view is invoked. So far so good, but what are the
stuff happening below in chronology, I was unable to get, also is
there any redundancy, read the comments for the queries:
// the div with id search_container will be picked up by el
<div id="search_container"></div>
<script type="text/javascript">
SearchView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
//1. Compile the template using underscore, QUESTION: **what does this mean**?
var template = _.template( $("#search_template").html(), {} );
//2. Load the compiled HTML into the Backbone "el"
this.el.html( template );
}
});
// QUESTION: **When in 2. we have already mentioned, el, can we not over there
provide the div element?**
var search_view = new SearchView({ el: $("#search_container") });
</script>
<script type="text/template" id="search_template">
<label>Search</label>
<input type="text" id="search_input" />
<input type="button" id="search_button" value="Search" />
</script>
Question 1
Compiling template means that you get a template function that you can pass in a data object as argument of the template function. That way, you can evaluate many times your template function with different data objects while it's compiled only once.
compiledTemplate = _.template( $("#search_template").html());
this.el.html(compiledTemplate(data));
another.el.html(compiledTemplate(otherData));
In the example above you compile your template once, then you use it twice.
In the code you have given, you compile and use your template at the same time
So
_.template( $("#search_template").html()); // Create a template function, the result is a function that you can call with a data object as argument
_.template( $("#search_template").html(), data); // Create a template function and call it directly with the data object provided, the result is a string containing html
Ref: http://underscorejs.org/#template
Question 2
You can provide the div element directly, but el is an helper to retrieve the root element of your view on which all DOM events will be delegated.
As indicated in the backbone doc
If you'd like to create a view that references an element already in
the DOM, pass in the element as an option: new View({el:
existingElement})
Ref: http://backbonejs.org/#View-el

Categories