Evan Emolo
Evan Emolo

Reputation: 1670

Backbone .fetch() not updating collection with the latest data

I am attempting to update a filtered video collection when a user model makes a request to the server. The problem is in the update method. fetch() does not get the latest data for the video collection.

What do I need to fix in update to get the latest data so the view re-renders properly?

var VideosView = Backbone.View.extend({
  tagName: 'ul',
  id: 'video-list',

  initialize: function() {
    var user = this.model;
    var _videos = user.get('videos');
    user_vids = videos.filter_videos(_videos);

    this.listenTo(user, 'request', this.update);
  },

  render: function() {
    user_vids.each( this.addOne, this );
    return this;
  },

  addOne: function(video) {
    var videoView = new VideoView({ model: video });
    this.$el.append( videoView.render().el );
  },

  update: function() {
    $('#video-list').empty();
    _videos = this.model.get('videos');
    videos.fetch();
    user_vids = videos.filter_videos(_videos)
    user_vids.each( this.addOne, this );
  }
});

// Instantiate
var videosView = new VideosView({
  model: user,
  collection: videos
});
$('#allVideos').append( videosView.render().el );

EDIT

Adding additional code:

Here is where videosView is initialized.

var IndexView = Backbone.View.extend({
  initialize: function() {
    user = this.model;
  },

  render: function() {
    var videosView = new VideosView({
      model: user,
      collection: videos
    });
    $('#allVideos').append( videosView.render().el );

    var addVideoView = new AddVideoView({
      model: user,
      collection: videos
    });
    $('#addVideo').append( addVideoView.render().el );
  }
});

The listenTo in VideoViews is listening for what is happening in add_to_user_array in the AddVideoView here:

var AddVideoView = Backbone.View.extend({
      tagName: 'div',

      template: _.template( AddVideoTemplate ),

      events: {
        'click #videoSubmitButton': 'submit'
      },

      initialize: function() {
        user = this.model;
      },

      render: function() {
        var template = this.template();
        this.$el.html(template);
        return this;
      },

      submit: function(event) {
        event.preventDefault();

        console.log('form submitted');

        var vimeo_id = parseInt( $('#vimeo_id').val() );
        var newVideo = {vimeo_id: vimeo_id};

        this.collection.create(newVideo, {wait: true});
        this.add_to_user_array(vimeo_id);
      },

      add_to_user_array: function(vimeo_id) {
        var _videos = user.get('videos');
        _videos.push(vimeo_id);
        user.save({videos: _videos}, {wait: true});
      }
    });

Inside the router I'm instantiating the model and collection:

index: function() {
  users = new Users;
  users.fetch({
    success: function(user_data) {
      var user = user_data.findWhere({username: App.username})
      videos = new Videos;
      videos.fetch({
        success: function(video_data) {
          var indexView = new IndexView({
            model: user,
            collection: videos
          });
          var indexController = new IndexController;
          indexController.showView( indexView );
        }
      });
    }
  })
}

Upvotes: 2

Views: 1402

Answers (2)

Sushanth --
Sushanth --

Reputation: 55740

First fetch is Asynchronous

So you are trying to filter the collection immediately after the fetch request. But the statements following them will simply execute immediately without waiting for the new data. You need to listen to the reset event or the sync event of the collection if you want the new data in hand. Also it is a better idea if you attach the common attributes to the view directly using this .. Try this

var VideosView = Backbone.View.extend({
    tagName: 'ul',
    id: 'video-list',

    initialize: function () {
        // Model
        this.user = this.model;
        // collection passed in
        this.videos = this.collection;
        // videos on model
        this._videos = user.get('videos');
        // filtered collection
        this.user_vids = videos.filter_videos(this._videos);
        // Listen to sync request on model
        this.listenTo(user, 'sync', this.update);
        // Listen to the video collection 
        // which will call the method once the collection is refreshed
        this.listenTo(this.videos, 'reset', this.resetVideos);
    },
    update: function () {
        $('#video-list').empty();
        _.delay( function() {
            // You already have access to videos
            // in the form of this.videos
            this.videos.fetch({reset: true});
        }, 100 );
    },
    resetVideos: function () {
        // Build the new user_vids collection from
        // newly fetch videos collection
        this.user_vids = this.videos.filter_videos(this._videos)
        this.user_vids.each(this.addOne, this);
    },
    addOne: function (video) {
        var videoView = new VideoView({
            model: video
        });
        this.$el.append(videoView.render().el);
    },
    render: function () {
        this.user_vids.each(this.addOne, this);
        return this;
    }
});

Let me know if the notes make sense or I misunderstood somewhere.

Upvotes: 2

willz
willz

Reputation: 2050

You should be listening to "sync" instead of "request"

request — when a model (or collection) has started a request to the server

sync — when a model (or collection) has been successfully synced with the server

by listening to request, you will only see the current data at the time of the request, not when the data has been synced and the collection updated.

Upvotes: 0

Related Questions