Josh Earl
Josh Earl

Reputation: 18351

Backbone.js collection view rendering duplicate items

In my Backbone.js app, I need to be able to insert new items between existing items in a collection. The examples I have found thus far on the web all seem to assume that new items should be appended to the end of a collection. That doesn't work for my purposes, so I've opted to rerender the entire collection when a new item is added.

However, the original items aren't removed from the view; instead, a duplicate set of items is appended to the end of the list. I'm able to work around this by using jQuery to clear the items prior to rendering, but that seems wrong.

Here's what I have right now:

Item = Backbone.Model.extend({
    price: null,
});

Items = Backbone.Collection.extend({
    model: Item, 
    initialize: function () {
      this.add(new Item({ price: '$0.50' }));
      this.add(new Item({ price: '$0.60' }));
      this.add(new Item({ price: '$0.70' }));
    }
});

ItemView = Backbone.View.extend({
    tagName: 'li',
    initialize: function () {
      this.model.bind('change', this.render, this);
    },

    render: function () {
      var item_template = _.template($('#item-template').html(), { item: this.model }); 
      this.$el.html(item_template);
      return this;
    },

    events: {
      "click .copy-item": "copyItem",
    },

    copyItem: function (event) {
      var index = itemlistview.collection.indexOf(this.model);
      itemlistview.collection.add(new Item, { at: index + 1 });
    },
});

ItemListView = Backbone.View.extend({
    el: '#item-rows',
    initialize: function () {
      _.bindAll(this);
      this.collection = new Items();
      this.collection.bind('add', this.render);
      this.render();
    },
    render: function () {
      // It works if I uncomment the line below
      //$('.item-row').remove();
      var self = this;
      this.collection.each(function (item) {
        self.$el.append(new ItemView({ model: item }).render().el);
      });
      return this;
    },
});

var itemlistview = new ItemListView;

And here's a jsFiddle that demonstrates the problem.

Is there a better way to handle this?

Upvotes: 4

Views: 4205

Answers (1)

Dogbert
Dogbert

Reputation: 222188

As you're rerendering the whole thing, you do need to clear the old rendered inputs.

http://jsfiddle.net/Zk9NX/8/

Altered ItemListView

ItemListView = Backbone.View.extend({
    el: '#item-rows',
    initialize: function () {
      _.bindAll(this);
      this.collection = new Items();
      this.collection.bind('add', this.render);
      this.render();
    },
    render: function () {
      var self = this;
      this.$el.empty()
      this.collection.each(function (item) {
        self.$el.append(new ItemView({ model: item }).render().el);
      });
      return this;
    },
});

Upvotes: 4

Related Questions