使用Backbone.js将远程集合渲染到页面上

时间:2014-04-27 21:55:37

标签: javascript ruby-on-rails ruby backbone.js coffeescript

我使用Backbone.js和Ruby on Rails来渲染帖子集合,但屏幕上没有任何内容呈现。

应用文件

#= require_self
#= require_tree ./templates
#= require_tree ./models
#= require_tree ./views
#= require_tree ./routers

window.IntroProject =
  Models: {}
  Collections: {}
  Routers: {}
  Views: {}

  init: ->
    window.router = new IntroProject.Routers.HomepageRouter({})
    Backbone.history.start()

$ ->
  IntroProject.init()

模型和集合

class IntroProject.Models.Post extends Backbone.Model
  paramRoot: 'post'

class IntroProject.Collections.PostsCollection extends Backbone.Collection
  model: IntroProject.Models.Post
  url: '/'

路由器

class IntroProject.Routers.HomepageRouter extends Backbone.Router

  initialize: (options) ->
    @posts = new IntroProject.Collections.PostsCollection()
    @posts.fetch()


  routes:
    '':'index'

  index: ->
    @view ||= new IntroProject.Views.Posts.IndexView(collection: @posts)
    @view.render()

查看

IntroProject.Views.Posts ||= {}

class IntroProject.Views.Posts.IndexView extends Backbone.View

  el: '#posts'

  template: _.template( $('#home-post-template').html() ) if $("#home-post-template").length

  render: ->
    @$el.append( @template() )
    @
    # _.each(@collection, (post) =>
    #   postHtml = $(@template(post))
    #   @$el.append( postHtml )
    # )
    # @

模板

  <script type="text/template" id="home-post-template">
  <div class="row">
    <div class="small-12 columns">
      <h2 class="blog-post-title"><%= post.title %></h2>
      <small>
        <p class="blog-post-meta">Published on <%= post.created_at.strftime('%B %d, %Y') %>
          <% if (post.user.email) %>
          by <strong><%= post.user.email %></strong>
          <% end %>
        </p>
      </small>
      <hr>
      <div class="blog-post-body">
        <p><%= post.body.html_safe %></p>
      </div>
    </div>
  </div>
  </script>

我可以在加载页面时调用render()因为我执行了console.log('hello')。我甚至做过console.log(@collection)。输出:

PostsCollection {models: Array[0], length: 0, _byId: Object, constructor: function, model: function…}
_byId: Object
_idAttr: "id"
length: 2
models: Array[2]
__proto__: ctor

所以他们的帖子都在收藏中。

当我@$el.append( @template() )时,它post未定义,所以我@$el.append( @template(post: @collection.models)。但该帖子未定义。

总的来说,我了解发生了什么,但它不起作用。我错过了一些重要的东西。这是my github repo

更新

答案中建议的更改

路由器

class IntroProject.Routers.HomepageRouter extends Backbone.Router

  initialize: (options) ->
    @posts = new IntroProject.Collections.PostsCollection()
    @posts.fetch({ reset : true })

  routes:
    '':'index'

  index: ->
    @view ||= new IntroProject.Views.Posts.IndexView(collection: @posts)

查看

IntroProject.Views.Posts ||= {}

class IntroProject.Views.Posts.IndexView extends Backbone.View

  el: '#posts'

  template: _.template( $('#home-post-template').html() ) if $("#home-post-template").length

  initialize: ->
    @listenTo(@collection, 'reset', @render)

  render: ->
    _.each(@collection, (post) ->
      postHtml = @template( post )
      @$el.append( postHtml )
    , @)

模板,我从post

中删除了post.title

现在我通过控制台获得Uncaught ReferenceError: title is not defined

2 个答案:

答案 0 :(得分:0)

您的集合是异步获取的,这意味着当您在视图上调用render时,数据尚未存在。您可以在console.log(@collection)中看到它:length为0。

您的视图需要侦听其集合并在(从)更改/从服务器返回时重新呈现。您可以将此函数添加到您的视图类(不熟悉CoffeeScript,因此编写JS):

initialize : function(){
  this.listenTo(this.collection, 'reset', this.render);    
}

但是,当fetch call返回时未显式触发重置事件,请将其更改为此(在路由器的设置中):

@posts.fetch({ reset : true });

此外,您观看的render功能看起来不对我,我写这个

render : function(){
  _.each(this.collection, function(model){
    var html = this.template( model.toJSON() );
    this.$el.append( html );
  }, this);
}

这也意味着您需要在模板中替换post的引用:

  • 替换`&lt;%= post.title%&gt;
  • 使用<%= title %>

答案 1 :(得分:0)

我能够在一些帮助下找到答案。这是固定代码的样子。如果没有进行任何更改,则省略

路由器

class IntroProject.Routers.HomepageRouter extends Backbone.Router

  initialize: (options) ->
    @posts ||= new IntroProject.Collections.PostsCollection()

  routes:
    '':'index'

  index: ->
    @view ||= new IntroProject.Views.Posts.IndexView(collection: @posts)
    @posts.fetch()

查看

IntroProject.Views.Posts ||= {}

class IntroProject.Views.Posts.IndexView extends Backbone.View

  el: 'body'

  template: _.template( $('#home-post-template').html() ) if $('#home-post-template').length

  initialize: ->
    @collection.bind "reset", ->
      @render()
    , @

  render: ->
    @collection.each (post) =>
      $('#posts').append( @template(post.attributes) )
    @

模板

 <script type="text/template" id="home-post-template">
  <div class="row">
    <div class="small-12 columns">
      <h2 class="blog-post-title"><%= post.title %></h2>
      <small>
        <p class="blog-post-meta">Published on <%= post.created_at %>
          <% if (post.user.email) %>
          by <strong><%= post.user.email %></strong>
          <% end %>
        </p>
      </small>
      <hr>
      <div class="blog-post-body">
        <p><%= post.body %></p>
      </div>
    </div>
  </div>
  </script>

问题出在fetch和render调用上。当路由器初始化时,我正在获取集合。该集合不会持久存在于视图中。当我在视图上调用render时,没有任何东西可以渲染。

在解决方案中,我现在在初始化视图后拥有fetch()。初始化视图后,一旦fetch()成功离开reset事件,视图就会呈现。我选择将el设为body,因为我的活动需要elbody。事件在el内查看何时触发。