1
votes

I have a backbone collection and when I remove a model from the collection, I want it to remove the item from a list in the view.

My collection is pretty basic

MyApp.Collections.CurrentEvents = Backbone.Collection.extend({
    model: MyApp.Models.Event
});

and in my views I have

MyApp.Views.CurrentEventItem = Backbone.View.extend({
   el: 'div.current_event',

   initialize: function(){
        event = this.model;
        _.bindAll(this, "remove");
        MyApp.CurrentEvents.bind('remove',this.remove); //the collection holding events

     this.render();
  },

   // yeah, yeah, render stuff here

 remove: function(){
    console.log(this);
    $(this.el).unbind();
    $(this.el).remove();
  }
});

when I remove the model from the collection, it triggers the remove function, but the view is still on the page. In the console, I can see the model, but I believe the model should have an 'el', but it doesn't.

My container code is

MyApp.Views.CurrentEventsHolder = Backbone.View.extend({
    el: 'div#currentHolder',

    initialize: function(){
           MyApp.CurrentEvents = new MyApp.Collections.CurrentEvents();
           MyApp.CurrentEvents.bind('new', this.add);
   },
   add: function(){  
        var add_event = new MyApp.Views.CurrentEventItem(added_event);
        $('div#currentHolder').append(add_event.el);

     }
});

for some reason in the add method I can't seem to use the $(this.el) before the append, though I'm not sure if that is the problem.

2
You output $(this.el) from within the remove function? The way your function is written, it should be removing all the event views when collection.remove happens.Josh Leitzel
Why aren't you using this.bind in your initialize method? What happens if you set a breakpoint in the remove method and manually type in the $(this.el).remove()tkone
@JoshLeitzel why should it be removing all of my views? I believe backbone knows which model has been deleted and should remove only that one.pedalpete
@tkone I'll try using this.bind, but it will have to wait until I get to work tomorrow morning. I have no idea how to manually type $(this.el).remove into a breakpoint. can you elaborate on that?pedalpete
In chrome or safari launch the web inspector and go to the scripts tab. Click on the line number next to the place you want to pause script execution. See this: code.google.com/chrome/devtools/docs/scripts-breakpoints.html In Firefox you'll need to install firebug and then read this: getfirebug.com/doc/breakpoints/demo.html. It allows you to stop code execution at an explicit point and check the current execution context and stack, as well as the values of any variables and attributes available within the execution context.tkone

2 Answers

2
votes

PROBLEM: MyApp.CurrentEvents.bind('remove',this.remove);

This triggers the remove() function every time any model is deleted from the collection.

This means that anytime a model is deleted, all the CurrentEventItem view instances will be deleted.

Now, about the view still being on the page:

It must have something to do with the way you appended/added/html-ed the view in the page. Check your other views, maybe if you have a CurrentEventsContainer view of some sort, check your code from there because with your current code, it does delete the view, albeit, all of them though.

RECOMMENDED FIX:

change your bindings to:

this.model.bind('remove',this.remove);

and make sure that when you instantiate it, pass on the model so that each view will have a corresponding model to it like so:

//...
addAllItem: function(){ 
      _.each(this.collection, this.addOneItem())
},
addOneItem: function(model){
      var currentEventItem = new MyApp.Views.CurrentEventItem({model:model});
      //...more code..
}
//...

This makes things a lot easier to manage in my opinion.

UPDATE (from your updated question)

The reason you aren't able to use this.el is because you haven't passed the right context into the add() function. You need to add _.bindAll(this,'add') in your initialize() function to pass the right context, therefore making your this correct, allowing you to use this.el within the add function. Also, change your code to this:

MyApp.CurrentEvents.bind('new', this.add, this); this passes the right context. Also, why not use add instead as an event?

2
votes

Continuing what I said in the comments, the way you've implemented this right now will remove all the CurrentEventItem views from the page when any of them is removed from the collection. The reason for this is the following:

MyApp.CurrentEvents.bind('remove',this.remove);

What this essentially says is, "every time the remove event is triggered on the collection, call this.remove." So, every time you instantiate one of these views, you're also telling the collection to remove that view when the collection triggers a remove event. I've created a fiddle to show you the problem.

You're right that Backbone knows which model has been removed from a collection, but you're not taking advantage of that. You can do that like so:

  removeFromView: function(model) {
    // Check to make sure the model removed was this.model
    if (model.cid === this.model.cid) {
      $(this.el).unbind();
      $(this.el).remove();
    }
  }

See how this minor adjustment changes the behavior? Check it out in action here.

If you follow this pattern, you should see the proper views being removed.