0

I'm playing around with code from the Code School Backbone tutorial, and some of the code that I'm using from their examples that I've adapted for my own purposes doesn't seem to work. Basically, I've added a listener to add a new model to a collection, which works fine, but when I added the remove listener, it seems to delete all of my view. I think that the problem is related to the "el: '.monster'" in my view, but I haven't figure out the right mix to fix it.

Here is the code:

// MODEL
var Monster = Backbone.Model.extend({
    defaults: {
        name: '',
        health: '',
        defense: '',
        attack: '',
        damage: ''
    }
});

// COLLECTION
var MonsterList = Backbone.Collection.extend({
    model: Monster,
    url: '/monsters',
    initialize: function() {
        this.on('remove', this.hideModel);
    },
    hideModel: function(model) {
        model.trigger('hide');
    }
});

var monsterList = new MonsterList();

var monsters = [
    {name: 'Gobby', health: 10, defense: 10, attack: 5, damage: 4},
    {name: 'Clobber', health: 15, defense: 10, attack: 7, damage: 4},
    {name: 'Gumms', health: 9, defense: 10, attack: 5, damage: 2}
];

monsterList.reset(monsters);

// VIEW

var MonsterView = Backbone.View.extend({
    el: '.monster',
    template: _.template('<table>' +
        '<th><%= name %></th>' +
        '<tr><td>Health</td> <td><%= health %></td>' +
        '<td>Defense</td><td><%= defense %></td></tr>' +
        '<tr><td>Attack</td><td><%= attack %></td>' +
        '<td>Damage</td><td><%= damage %></td><tr>' +
        '</table>'
        ),
    initialize: function() {
        this.model.on('hide', this.remove, this);
    },
    remove: function() {
        this.$el.remove();
    },
    render: function(){
        this.$el.append(this.template(this.model.toJSON()));
    }
});

var MonsterListView = Backbone.View.extend({
    initialize: function() {
        this.collection.on('add', this.addOne, this);
        this.collection.on('reset', this.addAll, this);
    },
    addOne: function(monster) {
        var monsterView = new MonsterView({model: monster});
        this.$el.append(monsterView.render());
    },
    addAll: function() {
        this.collection.forEach(this.addOne, this);
    },
    render: function() {
        this.addAll();
    }
});

var monsterListView = new MonsterListView({collection: monsterList});
monsterListView.render();

The html file is just an empty div with the class 'monster'. Anything to help steer me in the right direction would be greatly appreciated!

EmptyArsenal
  • 7,314
  • 4
  • 33
  • 56

2 Answers2

1

Yes, your suspicion is correct, the 'el' property is the problem.

When you supply a value for 'el' as part of your Backbone.View Class definition, EVERY instance of that View will be attached to the first DOM element that matches that class/id.

So when you create 3 MonsterViews, they all get assigned to the same element, and thus when one is removed, all 3 are.

To fix this, remove the 'el' setting from the MonsterView Class, and instead pass a unique 'el' reference for each new instance.

Check the addOne method below:

// MODEL
var Monster = Backbone.Model.extend({
    defaults: {
        name: '',
        health: '',
        defense: '',
        attack: '',
        damage: ''
    }
});

// COLLECTION
var MonsterList = Backbone.Collection.extend({
    model: Monster,
    url: '/monsters',
    initialize: function() {
        this.on('remove', this.hideModel);
    },
    hideModel: function(model) {
        model.trigger('hide');
    }
});

var monsterList = new MonsterList();

var monsters = [
    {name: 'Gobby', health: 10, defense: 10, attack: 5, damage: 4},
    {name: 'Clobber', health: 15, defense: 10, attack: 7, damage: 4},
    {name: 'Gumms', health: 9, defense: 10, attack: 5, damage: 2}
];

monsterList.reset(monsters);

// VIEW

var MonsterView = Backbone.View.extend({

    template: _.template('<table>' +
        '<th><%= name %></th>' +
        '<tr><td>Health</td> <td><%= health %></td>' +
        '<td>Defense</td><td><%= defense %></td></tr>' +
        '<tr><td>Attack</td><td><%= attack %></td>' +
        '<td>Damage</td><td><%= damage %></td><tr>' +
        '</table>'
        ),
    initialize: function() {
        this.model.on('hide', this.remove, this);
    },
    remove: function() {
        this.$el.remove();
    },
    render: function(){
        this.$el.html(this.template(this.model.toJSON()));
    }
});

var MonsterListView = Backbone.View.extend({
    el: '#monsterList',
    initialize: function() {
        this.collection.on('add', this.addOne, this);
        this.collection.on('reset', this.addAll, this);
    },
    addOne: function(monster) {
        var newEl = this.$el.append('<div></div>');            
        var monsterView = new MonsterView({model: monster, el: newEl});
        monsterView.render();
    },
    addAll: function() {
        this.collection.forEach(this.addOne, this);
    },
    render: function() {
        this.addAll();
    }
});

var monsterListView = new MonsterListView({collection: monsterList});
monsterListView.render();

JS Bin Example

providencemac
  • 612
  • 6
  • 14
  • Thanks for the response. Your explanation makes complete sense; however, when I load the page, it no longer renders the views and I just have a blank page. Any ideas? – EmptyArsenal Sep 22 '13 at 23:35
  • inside your html? – jrsalunga Sep 22 '13 at 23:39
  • Yes, that's it in my html. – EmptyArsenal Sep 22 '13 at 23:42
  • I'm not sure if this is helpful, but when I load the page (which is blank), if I enter 'monsterListView.el' in the console, it appear that the new view is not created within the new div tag, but after it. – EmptyArsenal Sep 22 '13 at 23:52
  • The standard "let each view create its own `el` and say `$(x).append(view.render().el)`" pattern is your best bet to avoid these sorts of problems. – mu is too short Sep 22 '13 at 23:54
  • I changed the MonsterView render method to use `$.html()` instead of `$.append()` perhaps that will fix it... I'll see if I can setup a JSFiddle to test it out – providencemac Sep 23 '13 at 00:01
  • I added a JS Bin example - Make sure the `MonsterViewList` is assigned to an element, either in the Class declaration or when you create an instance. – providencemac Sep 23 '13 at 00:06
  • The original view shows again, but the remove doesn't work properly. If I do the following: var goblin = new Monster({name: 'Bob', health: 10, defense: 10, attack: 5, damage: 4}); monsterList.add(goblin); monsterList.remove(goblin); It removes the entire collection view. – EmptyArsenal Sep 23 '13 at 00:20
  • try monsterList.remove(monsterList.at(2)) – jrsalunga Sep 23 '13 at 00:44
  • I got it! Thanks everyone for the assistance. I changed the addOne function to the following: addOne: `function(monster) { var monsterView = new MonsterView({model: monster}); this.$el.append(monsterView.render().el); }` And it seemed to do the trick. – EmptyArsenal Sep 23 '13 at 01:31
0

try to put this at the bottom

$(document).ready(function(){

monsterList.remove(monsterList.at(2));
var monsterListView = new MonsterListView({collection: monsterList});
monsterListView.render();

});

i think this is the good way on how to delete/remove a view.. not

 monsterListView.remove(goblin) 
jrsalunga
  • 409
  • 2
  • 8
  • 20