Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

backbone.js collection not properly removing item in view

I'm having some trouble deleting an item from a collection inside a model when in a view. Basically the model/collection structure is the following:

enter image description here

Basically when I try to remove an item from the sub item collection in the sub item view it actually removes the correct item from the collection. However when I come to persisting the main model the item seems to be still in the collection.
This is the how my views are structured:

enter image description here

The main view inserts the DOM nodes required by the main model, and them main model creates a new view for the item model etc. All views are getting the main model as model option like so:

new App.Views.MainModelView({
  model : this.model,
  el : $('#nodeID')
})

The only difference is in the creation of the Sub-item model view, where, due to re usability of the view and template, I still pass in the main model, however I also pass in the item in the item collection that is currently being modified. Which looks like this:

new App.Views.ItemView({
  model : this.model,
  item : this.selectedItem,
  el : $('#nodeID')
});

In the sub-item's view init method I do the following:

this.item = (this.options.item) ? this.options.item : this.model;

To remove a sub-item from the sub-item collection I do:

removeSubItem : function(e) {
  // get the id of the sub-item to be removed
  var id = $(e.target).closest('tr').attr('data-id');
  if (!id) throw  "Could not retrieve data id";
  // retrieve the sub-item from the collection
  var subItem = this.item.subItems.get(id);
  // remove the sub-item from the collection
  this.item.subItems.remove(subItem);
},

As I said earlier when I remove the sub-item and I inspect the collection modified by the view I can see that the sub-item has been removed from the collection, however then I persist the main model the removed sub-item re-appears. The leads me to believe that somewhere along the line the sub-item collection might be cloned which could explain the sudden reappearance of the sub-item.

I know this is a fairly specific problem and I'm not sure if it is possible to get to the cause of the problem with what I have provided here, if you need any more information please let me know.

Thanks for all your help,

Vincent

========== EDIT ============

To answer some of the questions below let me outline the scope in which I am experiencing this issue:

If I console log the this.item.subItems collection in the SubItem view, after removeSubItem was called, I can see that the instance of the SubItem model has been removed successfully. Before I call the save method on the main model I console log the return of the toJSON function. At this point I am experiencing the problem that the previously removed instance is 'back' in the collection. I have been monitoring the traffic between client and server with both Wireshark and Google chrome's developer console and there is no call to the server to refresh any of the models.

The toJSON method for the SubItem collection looks like this:

toJSON : function() {
  App.log(["SubItem::collection::toJSON", this], "info");
  var json = {};
  // make sure the key for each SubItem is the primary key
  this.each(function(subItem) {
    json[subItem.get('id')] = subItem.toJSON();
  });

  return json;
}
like image 266
luxerama Avatar asked Jul 13 '11 10:07

luxerama


2 Answers

Backbone.js support for nested collection/models is non-existent, and they provide no saving support (see http://documentcloud.github.com/backbone/#FAQ-nested). You have to override toJSON on any model with a subcollection. I've run into this scenario a million times. If you have something like (in coffeescript):

class MainModel extends Backbone.Model

    itemCollection: ->
        @_itemCollection ?= new ItemCollection(@get('itemCollection'))


class ItemCollection extends Backbone.Collection

    model: ItemModel


class ItemModel extends Backbone.Model

    subCollection: ->
        @_subCollection ?= new SubCollection(@get('subCollection'))


class SubCollection extends Backbone.Collection

    model: SubModel


class SubModel extends Backbone.Model


mainModel = new MainModel(json)

Then in order for mainModel.save() to work, you need to override toJSON on MainModel and ItemModel, like:

class MainModel extends Backbone.Model

    itemCollection: ->
        @_itemCollection ?= new ItemCollection(@get('itemCollection'))

    toJSON: ->
        return _.extend(@attributes, {itemCollection: @itemCollection().toJSON()})


class ItemModel extends Backbone.Model

    subCollection: ->
        @_subCollection ?= new SubCollection(@get('subCollection'))

    toJSON: ->
        return _.extend(@attributes, {subCollection: @subCollection().toJSON()})

I wrote the example in coffeescript because it's much more concise than javascript. If you need any help making sense of it, please just ask.

Hope this helps!

--- Note ---

Technically, in coffeescript, the toJSON methods could simply be:

toJSON: ->
    _.extend @attributes, itemCollection: @itemCollection().toJSON()

But I wrote it the way I did to be more understandable to non-coffeescripters.

like image 190
Steve Avatar answered Oct 07 '22 01:10

Steve


Without looking at your whole code base I think you might have your structure slightly wrong. Normally with backbone I hardly ever if ever pass the :el element directly into the view. The view is responsible for generating it's own el. After it has been rendered I then insert the new view.el into the DOM. Like below

var subView = new FooView({ model: fooModel });

mainView.$(".list").append(subView.el);

In the above case there is a backbone object with every subview. If you need to remove the subview you don't need to do a selector query to find it, you just call the remove method on the object and it knows how to remove itself from the dom.

Or to be more specific the subView handles a click event on itself which it can then handle by destroying it's associated model and then calling remove on itself

like image 24
bradgonesurfing Avatar answered Oct 07 '22 03:10

bradgonesurfing