2012-06-12 30 views
2

我一直在寻找,发现很多答案,但似乎没有工作。Underscore.js模板问题 - 无法调用方法'替换'null

<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> 

首先;

创建方法

var newPhoto = new Photo(); 
newPhoto.setTitle('Fishing'); 

这项工作很好的一个新实例,它将通过模板加载到体内。但是,如果我那么做一遍,

newPhoto.setTitle('Sailing'); 

我得到的错误 - “无法调用空的‘取代’”

号线的错误,但我相信这是在

var template = _.template($('#rtemp').html(), {title:name}); 

回答

23

这里有一些错误。

  1. 您的模板已在type属性的双重",它应该是:

    <script id="rtemp" type="text/x-underscore-template"> 
    
  2. 你的观点的render被引用myPhoto当它应该是this.model

    var name = this.model.get('title'); 
    
  3. 而你的主要问题是你的观点使用<body>作为它的this.el而您的模板在<body>之内。

    $(this.el).html(template); 
    

    所以第一render电话后,没有更多的#rtemp

当你呈现你的看法你完全取代<body>内容。然后,在接下来的render电话,你试试这个:

var template = _.template($('#rtemp').html(), ...); 

但由于#rtemp不在DOM了,一切都分崩离析。

如果你抢立即模板:

var PhotoView = Backbone.View.extend({ 
    el: $('body'), 
    template: _.template($('#rtemp').html()), 
    //... 
}); 
render

,然后用this.template()

render: function(event) { 
    //... 
    var template = this.template({ 
     title: name 
    }); 
    //... 
} 

你有更好的运气。当然,您需要确保您使用此方法在文档就绪处理程序中定义视图,或者在定义视图时可能无法使用#rtemp

演示:http://jsfiddle.net/ambiguous/dwGsM/


这就是说,你的应用程序的结构是相当奇特。您有一个模型可以监听自己,然后当模型发生变化时模型创建并呈现视图。一个倾听自己的模型本身就很好,但通常你有视图倾听模型,视图会在模型更改时重新呈现自身(或者只是其自身的一部分)。

你的模型应该看起来更像是这样的:

var Photo = Backbone.Model.extend({ 
    setTitle: function(newTitle) { 
     this.set({ title: newTitle }); 
    }, 
    setLocation: function(newLoc) { 
     this.set({ location: newLoc }); 
    } 
}); 

然后你的看法是这样的:

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; 
    } 
}); 

initialize_.bindAll确保this.render将与右this被调用;然后initialize绑定到事件,以便它可以响应模型中的更改。该视图知道它关心的是什么,因此它负责处理模型中的更改。在render中,您通常只需拨打toJSON即可获取模板的数据。另外,Backbone的新版本在视图中包含$(this.el)的缓存版本,因此您不必再亲自使用$(this.el)

然后,你杀青事情是这样的:

var newPhoto = new Photo(); 
var viewPhoto = new PhotoView({ model: newPhoto }); 
newPhoto.setTitle('Fishing'); 
newPhoto.setTitle('Sailing'); 

你告诉视图创建视图时指定model选择使用什么型号的。

您可能还想将模板<script><body>中移出。

新的和改进的演示:http://jsfiddle.net/ambiguous/Kytw7/

+1

谢谢亩太短,已经帮助了很多。我一直在用这个骨干的东西抨击我的头,但你的解释已经清除了很多问题:D – mcclennon19

+0

优秀的解释! 1.我想知道,如果在var viewPhoto = new PhotoView({model:newPhoto})之后调用'render()';'你没有对模型进行任何修改。 2.您如何减少“渲染”次数? –

+0

@serbanghita:很难说没有更多的细节,但你可能只是手动调用它。 –