Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
315 views
in Technique[技术] by (71.8m points)

backbone.js - View's collection only working in console.log

Sorry for the vague title, I'm not sure what's going on enough to formulate it better.

So, this is the render in my view:

  render: function () {
    var that = this;
    var template = JST['gists/index'];
    that.$el.html(template);
    console.log(['index to render', that.collection]);
    _.each(that.collection, function (gist) { // <- I set a breakpoint here
      console.log('looping');
      that.appendGistDetail(gist);
    });

    return that;
  },

The `console.log(..., that.collection]) is logging this collection correctly:

["index to render", child]
  0: "index to render"
  1: child
    _byId: Object
    length: 1
    models: Array[1] // <- Note one element. Saving space, but I checked it out and the model is correct
// Worth noting 'looping' is not being logged

However the output from the previously mentioned breakpoint Scope Variable display in Chrome Dev tools:

that: child
  $el: jQuery.fn.jQuery.init[1]
  childViews: Array[0]
  cid: "view2"
  collection: child
    _byId: Object
    length: 0
    models: Array[0] // <- Note it's empty, just to test I also set a bp on the line above and it's the same, and when I hovered my mouse over `that.collection` from `console.log` it also said it was empty, but it logs correctly.

So, I'm not really sure what to do, or even what's going on.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So you set a breakpoint here:

_.each(that.collection, function (gist) {

and see that that.collection is empty but your console.log:

console.log(['index to render', that.collection]);

suggests that that.collection has one model. I suspect that you're seeing two confusing behaviors at the same time:

  1. console.log throws a live reference to its arguments into the console, it doesn't take a snapshot of their current state. So, if something changes between the console.log call and when you look at the console, you'll see the changed version rather than the one you think you logged.
  2. Collection#fetch is an AJAX call and A stands for Asynchronous.

So the sequence of events probably looks like this:

  1. You collection.fetch() which fires off an AJAX call.
  2. You say v = new View({ collection: collection }) and v.render().
  3. console.log is hit and a reference to the collection gets stashed in the log.
  4. You get to your breakpoint and find an empty collection.
  5. The server responds to the AJAX call and the collection gets populated with one model.
  6. You look at the console and find a collection with one model in it.
  7. Confusion.

The solution is to bind your rendering to events on the collection and make sure your render can handle an empty collection in a sensible fashion.

And be careful with using console.log to help you debug asynchronous things, you'll have to take your own snapshots (console.log(collection.toJSON()) for example) if you want consistent and sensible results.


PS: console.log is a variadic function, you can give it as many arguments as you want:

console.log('index to render', that.collection);

Also, a collection contains a list of models but it isn't actually the list itself. So doing things like _.each(collection, ...) won't spin you through the models, it will spine you through a bunch of internal properties that you probably don't care about. You want to iterate over the models:

_.each(collection.models, ....)

or better, use the built-in Underscore methods:

collection.each(...)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...