问题描述
我一直在寻找并找到了很多答案,但似乎都没有.
第一;
创建方法的新实例
var newPhoto = new Photo();newPhoto.setTitle('钓鱼');
这个工作正常,它将通过模板加载到正文中.但是,如果我再做一次,
newPhoto.setTitle('航行');
我收到错误 - 无法调用 null 的方法‘替换’"
没有线路错误,但我相信它在
var template = _.template($('#rtemp').html(), {title:name});
你在这里有一些错误.
你的模板在
type
属性中有一个双"
,它应该是:你的视图的
render
引用了myPhoto
而它应该是this.model
:var name = this.model.get('title');
你的主要问题是你的视图使用
作为它的
this.el
而你的模板在.
您在渲染视图时完全替换了 的内容:
$(this.el).html(template);
所以在第一个 render
调用之后,没有更多的 #rtemp
.然后,在下一个 render
调用中,你试试这个:
var template = _.template($('#rtemp').html(), ...);
但是由于 #rtemp
不再在 DOM 中,所以一切都分崩离析了.
如果您立即抓取模板:
var PhotoView = Backbone.View.extend({el: $('body'),模板:_.template($('#rtemp').html()),//...});
然后在render
中使用this.template()
:
render: function(event) {//...var 模板 = this.template({标题:姓名});//...}
你会有更好的运气.当然,您需要确保使用这种方法在文档就绪处理程序中定义视图,否则在定义视图时 #rtemp
可能不可用.
演示:http://jsfiddle.net/ambiguous/dwGsM/
也就是说,您的应用程序的结构相当奇怪.您有一个模型可以监听自身,然后模型会在发生变化时创建并呈现视图.监听自身的模型本身很好,但通常您有视图监听模型,并且随着模型的变化,视图会重新渲染自身(或只是自身的一部分).
您的模型应该更像这样:
var Photo = Backbone.Model.extend({设置标题:功能(新标题){this.set({ title: newTitle });},设置位置:函数(newLoc){this.set({ location: newLoc });}});
然后你的观点是这样的:
var PhotoView = Backbone.View.extend({el: $('body'),模板:_.template($('#rtemp').html()),初始化:函数(){_.bindAll(this, 'render');this.model.on('change:title', this.render);},渲染:函数(事件){这个.$el.html(this.template(this.model.toJSON()));返回这个;}});
_.bindAll
documentcloud.github.com/backbone/#View-constructor" rel="nofollow">initialize
确保 this.render
将使用正确的 ;然后 initialize
绑定到事件,以便它可以响应模型中的更改.视图知道它关心什么,所以它负责处理模型中的变化.在 render
中,您通常只需调用 toJSON
来获取模板的数据.此外,较新版本的 Backbone 在视图中包含缓存版本的 $(this.el)
作为 this.$el
这样你就不必再$(this.el)
自己了.
然后你会像这样:
var newPhoto = new Photo();var viewPhoto = new PhotoView({ 模型:newPhoto });newPhoto.setTitle('钓鱼');newPhoto.setTitle('航行');
您可以通过在创建视图时指定 model
选项来告诉视图使用什么模型.
您可能还想将模板 从
中移出.
新的和改进的演示:http://jsfiddle.net/ambiguous/Kytw7/>
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 thetype
attribute, it should be:<script id="rtemp" type="text/x-underscore-template">
Your view's
render
is referencingmyPhoto
when it should bethis.model
:var name = this.model.get('title');
And your main problem is that your view uses
<body>
as itsthis.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/
这篇关于Underscore.js 模板问题 - 无法调用 null 的方法“替换"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!