I have been looking over and found alot of answers but none seem to work.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Shopping Cart
</title>
<link rel="stylesheet" href="lib/style.css" type="text/css">
</head>
<body>
<script id="rtemp" type="text/x-underscore-template"">
<span><%= title %></span>
</script>
<script src="lib/jquery.js" type="text/javascript"></script>
<script src="lib/underscore.js" type="text/javascript"></script>
<script src="lib/backbone.js" type="text/javascript"></script>
<script src="lib/script.js" type="text/javascript"></script>
</body>
<script>
var Photo = Backbone.Model.extend({
initialize: function(){
console.log('this model has been initialized');
this.bind("change:title", function(){
var title = this.get("title");
console.log("My title has been changed to.. " + title);
var pv = new PhotoView();
pv.render();
});
},
setTitle: function(newTitle){
this.set({ title: newTitle });
},
setLocation: function(newLoc)
{
this.set({location:newLoc});
}
});
var PhotoView = Backbone.View.extend
({
el: $('body'),
render: function(event)
{
var name = myPhoto.get('title');
console.info(name);
var template = _.template($('#rtemp').html(), {title:name});
console.info(this.model);
$(this.el).html(template);
return this;
}
});
</script>
</html>
First;
Create a new instance of the method
var newPhoto = new Photo();
newPhoto.setTitle('Fishing');
This work fine, it will load into the body via the template. However if i then do it again,
newPhoto.setTitle('Sailing');
I get the error - "Cannot call method 'replace' of null"
No line error but I believe it is at
var template = _.template($('#rtemp').html(), {title:name});
You have a few things wrong here.
Your template has a double "
in the type
attribute, it should be:
<script id="rtemp" type="text/x-underscore-template">
Your view's render
is referencing myPhoto
when it should be this.model
:
var name = this.model.get('title');
And your main problem is that your view uses <body>
as its this.el
and your template is inside <body>
.
You completely replace the content of <body>
when you render your view:
$(this.el).html(template);
so after the first render
call, there is no more #rtemp
. Then, on the next render
call, you try this:
var template = _.template($('#rtemp').html(), ...);
but since #rtemp
isn't in the DOM anymore, everything falls apart.
If you grab the template immediately:
var PhotoView = Backbone.View.extend({
el: $('body'),
template: _.template($('#rtemp').html()),
//...
});
and then use this.template()
in render
:
render: function(event) {
//...
var template = this.template({
title: name
});
//...
}
you'll have better luck. You will, of course, need to make sure that you define your view inside a document-ready handler with this approach or #rtemp
might not be available when you define your view.
Demo: http://jsfiddle.net/ambiguous/dwGsM/
That said, the structure of your application is rather bizarre. You have a model which listens to itself and then the model creates and renders a view when something changes. A model listening to itself is fine in itself but usually you have views listening to models and the view would re-render itself (or just parts of itself) as the model changes.
Your model should look more like this:
var Photo = Backbone.Model.extend({
setTitle: function(newTitle) {
this.set({ title: newTitle });
},
setLocation: function(newLoc) {
this.set({ location: newLoc });
}
});
And then your view like this:
var PhotoView = Backbone.View.extend({
el: $('body'),
template: _.template($('#rtemp').html()),
initialize: function() {
_.bindAll(this, 'render');
this.model.on('change:title', this.render);
},
render: function(event) {
this.$el.html(
this.template(this.model.toJSON())
);
return this;
}
});
The _.bindAll
in initialize
ensures that this.render
will be called with the right this
; then initialize
binds to the event so that it can respond to changes in the model. The view knows what it cares about so it is responsible for dealing with changes in the model. And in the render
, you usually just call toJSON
to get the data for the template. Also, newer versions of Backbone include a cached version of $(this.el)
in the view as this.$el
so you don't have to $(this.el)
yourself anymore.
Then you'd crank things up like this:
var newPhoto = new Photo();
var viewPhoto = new PhotoView({ model: newPhoto });
newPhoto.setTitle('Fishing');
newPhoto.setTitle('Sailing');
You tell the view what model to use by specifying the model
option when creating the view.
You might also want to move the template <script>
out of <body>
.
New and Improved Demo: http://jsfiddle.net/ambiguous/Kytw7/