0
votes

I am working on a backbone app at the moment, and I have hit a bit of wall. I have collection that looks like this,

    r {length: 3, models: Array[3], _byId: Object, constructor: function, model: function…}
_byId: Object
_events: Object
_listenerId: "l40"
length: 3
models: Array[3]
__proto__: s

As you can see the collection has 3 models, and an example of the model is below,

    0: r
_changing: false
_events: Object
_pending: false
_previousAttributes: Object
attributes: Object
complete: false
completed_by: "0"
completed_by_name: null
end_date: false
files: r
id: "7693"
is_owner: false
item_active_task_count: 0
item_description: "sdsadasdasd"
item_files: r
item_id: "7693"
item_name: "First Item (1)"
item_tasks: r
item_type: null
numFile: 8
parent_id: null
progress: "0"
subItems: r
sub_items: Array[2]
tasks: r
__proto__: Object
changed: Object
cid: "c3"
collection: r
counter: 5
id: "7693"
__proto__: s

Within the model above I another collection called item_files, this collections looks like this,

    _byId: Object
length: 8
models: Array[8]
0: r
1: r
2: r
3: r
4: r
5: r
6: r
7: r
length: 8
__proto__: Array[0]
__proto__: s

and a model within this collection looks like this,

    models: Array[8]
0: r
_changing: false
_events: Object
_listenerId: "l48"
_pending: false
_previousAttributes: Object
attributes: Object
creator: "Me"
creator_id: "14"
download_url: "http://test.dev/projects/download/696d6167655f31322e4a5047/698/14"
file_created: "2014-06-12 00:00:00"
file_hex: "696d6167655f31322e4a5047"
file_parent_id: "7694"
file_parent_type_id: "7694"
id: "9011"
is_owner: 1
last_modified: "2014-06-12 00:00:00"
panel_name: "image_12.JPG"
project_id: "698"
size: "1.76 MB"
thumb_url: null
timeago: "a day ago"
user_type: ""
viewer_url: "http://test.dev/projects/viewer/698/696d6167655f31322e4a5047"
__proto__: Object
changed: Object
cid: "c12"
collection: r
id: "9011"
__proto__: s

What I am doing at the moment, is working on some delete functionality, I have a function that builds a container, and then lists models from the item_files collection, the view gets build like this,

addItems: function() {

    this.model.get('items').each(this.loadItem, this);

    //bind the expander
    $('.expander').unbind('click').click($.initExpanders.expander);


},

loadItem: function(item) {

    if(item.get("item_parent_id") == undefined) {
        var item_parent_id = item.get("item_id");
    }

    //item.set({"numFile" : item.get('item_files').length});

    var itemView = new app.ItemView({
        model: item,
        collection:item.get('item_files')
    });

    this.$el.find('.wrapper').append(itemView.render(item).el);
    if(item.get("subItems") !== undefined ) {
        if(item.get("subItems").length > 0) {
            if(item.get("subItems").length == 1) {
                this.$el.find(itemView.el).find('.sub-item-count').text(item.get("sub_items").length + " Sub Item"); 
            } else {
                this.$el.find(itemView.el).find('.sub-item-count').text(item.get("sub_items").length + " Sub Items");
            }
        }       
    }

    //itemView.addFilesWrapper(item);
    //itemView.addFiles();

    var itemFilesFilter = new app.FilesFilter({
        collection: item.get('item_files'),
        model: item
    });

    this.$el.find('article[data-item-id='+item.get('id')+'] .tab-content.files:first').html(itemFilesFilter.render().el);

    var that = this;
    item.get('files').each(function(file){
        var itemFileListItem = new app.FileListItem({
            model: file,
            collection: item.get('item_files'),
        });
        //console.log(that.$el.find('article[data-item-id='+item.get('id')+'] .tab-content').append(itemFileListItem.render().el));

        that.$el.find('article:first[data-item-id='+item.get('id')+'] .image-grid:first').append(itemFileListItem.render().el);
    });

},

so above lists out the models from the item collection, I thought I would able to listen to item collection for a change and then run any functionality I need from that, as change consists of and edit addition or deletion.

However if I remove a model from the collection the change event does not seem to trigger what is the problem?

1

1 Answers

1
votes

how are you removing the model from the collection? (sorry would comment this but don't have the rep required) EDIT -

here is an example of where the bubbling stops, in this example i have a cities(collection) who's model city has neighbourhoods(collection) who's model neighbourhod has people (collection)

removing a person from the neighbor can be detected by the people collection but not any further up, ill update this in a bit with how the event can be fired up the chain

var NeighbourHoodModel = Backbone.Model.extend({
  defaults:{
    name: null,
    people:null
  }
});
var PeopleModel= Backbone.Model.extend({
  defaults:{
    name: null
  }
});
var CityModel = Backbone.Model.extend({
  defaults:{
    neighbourhoods:null
  }
});
var NeighborhoodsCollection = Backbone.Collection.extend({
  model: NeighbourHoodModel
});
var PeopleCollection = Backbone.Collection.extend({
  model: PeopleModel
});
var CityCollection = Backbone.Collection.extend({
  model: CityModel
})


var cities = new CityCollection([
  { id:1, neighbourhoods: new NeighborhoodsCollection([
      { id:1,name: "neighbourhood-1",
        people: new PeopleCollection([
          { id:1,name: "jim"},
          { id:2,name: "fred"}
         ])
      }

     ]),
  }
]);

(function(cities){


  cities.forEach(function(city){
   city.get("neighbourhoods").get(1).get("people").on("remove", function(){


    console.log("person removed")
   });
   city.get("neighbourhoods").get(1).on("remove", function(){
    console.log("person removed from neighbourhood")
   });

   city.get("neighbourhoods").on("remove", function(){
    console.log("person removed from neighbourhoods")
   });
   city.get("neighbourhoods").get(1).get("people").remove(1);


});

})(cities); 

2ND EDIT - Ok so came up with a way to get properties of a model that are part of a collection to pass the event up. all you to do a pass in this and the attribute you want to listen to, it will then listen to that attribute for a remove, if it also a member of a collection it will fire a remove event for the collection. It will then also fir a change event on the attribute as well so you know that something in that model has changed. The original model removed and context are also available just like they would be with a normal remove event

this may be overkill though.

viewing the console on the pen view ((pen version of code)) you will see that the person remove event has been passed up the chain. you can remove the logMessage argument i just passed that in so you could see it working in the console.

the added function _bubbleEventToCollection

Backbone.Model.prototype.bubbleEventToCollection = function(caller, model, logMessage){
            this.listenTo(model, "remove", function(model, that, options) {
                console.log(logMessage, model);
                if (this.collection instanceof Backbone.Collection) {
                    this.collection.trigger("remove", model, that, options);
                }
                this.trigger("change", model, that, options);
            }, caller);
    };
    var PeopleModel = Backbone.Model.extend({
        defaults: {
            name: null
        },
        initialize: function() {
            this.on("remove", function(model) {
                console.log("person removed", model)
            }, this);
        }
    });
    var NeighbourHoodModel = Backbone.Model.extend({
        defaults: {
            name: null,
            people: null
        },
        initialize: function() {
            this.bubbleEventToCollection(this, this.attributes.people, "person removed from neighbourhood");
        }
    });
    var CityModel = Backbone.Model.extend({
        defaults: {
            neighbourhoods: null
        },
        initialize: function() {
              this.bubbleEventToCollection(this, this.attributes.neighbourhoods, "person removed from city");
        }
    });


    /**
     *
     * Collections
     */
    var NeighborhoodsCollection = Backbone.Collection.extend({
        model: NeighbourHoodModel,
        initialize: function() {
            this.on("remove", function(model) {
                console.log("person removed from neighbourhoods", model)
            }, this);
        }

    });
    var PeopleCollection = Backbone.Collection.extend({
        model: PeopleModel,
        initialize: function() {
            this.on("remove", function(model) {
                console.log("person removed from people", model)
            }, this);
        }

    });
    var CityCollection = Backbone.Collection.extend({
        model: CityModel,
        initialize: function() {
            this.on("remove", function(model) {
                console.log("person removed from cities", model)
            }, this);
        }
    })


    var cities = new CityCollection([{
        id: 1,
        neighbourhoods: new NeighborhoodsCollection([{
                id: 1,
                name: "neighbourhood-1",
                people: new PeopleCollection(
                    [{
                        id: 1,
                        name: "jim"
                    }, {
                        id: 2,
                        name: "fred"
                    }])
            }

        ]),
    }]);

    (function(cities) {
        cities.get(1).get("neighbourhoods").get(1).get("people").remove(1);
    })(cities);