I'm currently attempting to get upto speed with backbone.js, i figured the best way to do this is to get stuck into the online tutorials and documentation. The online tutorials and sample applications are excellent but in order to build by knowledge i'm attempting to build a sample website CRUD application of my own. For the sample, basically what i'm attempting to do is to merge two current online examples/tutorials. In an attempt to gain a better understanding of working with multiple models, collections and views.
Unfortunately i have gotten stuck... I apologies for the long winded explanation but as a novice i'm attempting to explain the issue as best as possible...
I have based my website application sample on the following tutorial:
https://github.com/ccoenraets/backbone-cellar/tree/master/bootstrap
View online example:
http://coenraets.org/backbone-cellar/bootstrap/
I was able to follow this tutorial and have a working version of the site. Now i wish to extent the application to contain more pages which fit into the application (backbone.js) structure. If you view the tutorial you will notice there is an 'about' page which simply loads a static html template into the application. What i would like to do is add a new page which displays a contact manager. The contact manager is deprived from the following tutorial:
http://net.tutsplus.com/tutorials/javascript-ajax/build-a-contacts-manager-using-backbone-js-part-1/
Please note: at this point in time for simplicity i'm only utilising part 1 of the tutorial.
Ok now to explain where i'm having the issue... Firstly i will outline what i have done. On the application i have added a new link in the headerView called Directory. On the main.js page (example of origianl: https://github.com/ccoenraets/backbone-cellar/blob/master/bootstrap/js/main.js) i have added the code as follows:
var AppRouter = Backbone.Router.extend({
routes: {
"" : "list",
"wines/page/:page" : "list",
"wines/add" : "addWine",
"wines/:id" : "wineDetails",
"about" : "about",
"directory" : "directory"
},
initialize: function () {
this.headerView = new HeaderView();
$('.header').html(this.headerView.el);
},
list: function(page) {
var p = page ? parseInt(page, 10) : 1;
var wineList = new WineCollection();
wineList.fetch({success: function(){
$("#content").html(new WineListView({model: wineList, page: p}).el);
}});
this.headerView.selectMenuItem('home-menu');
},
wineDetails: function (id) {
var wine = new Wine({id: id});
wine.fetch({success: function(){
$("#content").html(new WineView({model: wine}).el);
}});
this.headerView.selectMenuItem();
},
addWine: function() {
var wine = new Wine();
$('#content').html(new WineView({model: wine}).el);
this.headerView.selectMenuItem('add-menu');
},
about: function () {
if (!this.aboutView) {
this.aboutView = new AboutView();
}
$('#content').html(this.aboutView.el);
this.headerView.selectMenuItem('about-menu');
},
directory: function () {
if (!this.directoryView) {
this.directorytView = new DirectoryView();
}
$('#content').html(this.directoryView.el);
this.headerView.selectMenuItem('directory-menu');
}
});
utils.loadTemplate(['HeaderView', 'WineView', 'WineListItemView', 'AboutView', 'DirectoryView'], function() { app = new AppRouter(); Backbone.history.start(); });
Now for the Directory (Contacts Manger) page, for the sake of the explanation, i have left the model view and collection on the single .js file as per the tutorial - i would of course look to separate the file (into model and view) once i get it working. As per the tutorial the code for the contact manager (directory) is as follows:
//demo data
window.contacts = [
{ name: "Contact 1", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "family" },
{ name: "Contact 2", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "family" },
{ name: "Contact 3", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "friend" },
{ name: "Contact 4", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "colleague" },
{ name: "Contact 5", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "family" },
{ name: "Contact 6", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "colleague" },
{ name: "Contact 7", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "friend" },
{ name: "Contact 8", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "[email protected]", type: "family" }
];
//define product model
window.Contact = Backbone.Model.extend({
defaults: {
photo: "/img/placeholder.png"
}
});
//define directory collection
window.Directory = Backbone.Collection.extend({
model: Contact
});
//define individual contact view
window.ContactView = Backbone.View.extend({
tagName: "article",
className: "contact-container",
template: $("#contactTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
$(this.el).html(tmpl(this.model.toJSON()));
//alert('this model: ' + this.model.toJSON().name);
return this;
}
});
//define master view
window.DirectoryView = Backbone.View.extend({
el: $("#contacts"),
initialize: function () {
this.collection = new Directory(contacts);
this.render();
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.renderContact(item);
}, this);
},
renderContact: function (item) {
var contactView = new ContactView({
model: item
});
this.$el.append(contactView.render().el);
}
});
The changes i have made is simply remove the 'var' and replace with 'window.' to fit the existing stucture of the app. For example:
var DirectoryView = Backbone.View.extend({
becomes:
window.DirectoryView = Backbone.View.extend({
Now to the issue i'm having. I'm able to get the code to output (render) the html code to display the template.
I believe the issue is with the
//define individual contact view
window.ContactView = Backbone.View.extend({
tagName: "article",
className: "contact-container",
template: $("#contactTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
$(this.el).html(tmpl(this.model.toJSON()));
alert('this model: ' + this.model.toJSON().name);
return this;
}
});
Now i know that the data is being parsed correctly as the 'alert' is outputting the names correctly. The problem i'm having is the following line of code:
var tmpl = _.template(this.template);
is throwing the following error: "Uncaught TypeError: Cannot call method 'replace' of null".
I'm clueless on how to fix the issue :(
The DirectoryView.html template code is:
<div class="row">
<div class="span12">
<div id="contact"></div>
<script id="contactTemplate" type="text/template">
<img src="<%= photo %>" alt="<%= name %>" />
<h1><%= name %><span><%= type %></span></h1>
<div><%= address %></div>
<dl>
<dt>Tel:</dt><dd><%= tel %></dd>
<dt>Email:</dt><dd><a href="mailto:<%= email %>"><%= email %></a></dd>
</dl>
</script>
</div>
I hope i have supplied enough information. Please let me know if there is any more info required.
Thanks for having a look :)
Jake