Search code examples
javascriptjquerybackbone.js

Remove corresponding item when delete button is clicked. BackboneJs


I am currently learning BackboneJs and trying to understand how Backbone handles events. I have a simple list of items and each item has a delete button right next to it. I'm trying to figure out why the click event(Delete button) is registered in the console but the item is not removed. Here's what I have:

var Vehicle = Backbone.Model.extend();
var Vehicles = Backbone.Collection.extend({
    model: Vehicle
});


/*************
single view
**************/
var VehicleView = Backbone.View.extend({
    tagName: 'li',
    className: 'vehicle',
    render: function() {
        this.$el.html(this.model.get("title") + " Registration Number is: " + this.model.get("regiNum") + " <button class='delete-btn'>Delete</button>");
        this.$el.attr("id", this.model.id);
        return this;
    }
});

/*************
Collection View
*************/
var VehiclesView = Backbone.View.extend({
    tagName: "ul",
    initialize: function() {
        this.model.on('remove', this.vehicleRemove, this);
    },
    events: {
        "click .delete-btn": "vehicleRemove"
    },
    vehicleRemove: function(vehicle) {
        this.$("li#" + vehicle.id).remove()  // this is not working. the item is not being removed
        console.log('Delete button clicked')    // this is registered in the console log
    },

    render: function() {
        var self = this;
        this.model.each(function(vehicle) {
            var vehicleView = new VehicleView({
                model: vehicle
            });
            self.$el.append(vehicleView.render().$el);
        })
    }
});


var vehicles = new Vehicles([
    new Vehicle({
        id: 1,
        title: "Toyota",
        regiNum: "453454624"
    }),
    new Vehicle({
        id: 2,
        title: "Honda",
        regiNum: "daf4526"
    }),
    new Vehicle({
        id: 3,
        title: "Audi",
        regiNum: "jlkjfa34"
    })
])

var vehiclesView = new VehiclesView({
    el: "#container",
    model: vehicles
});
vehiclesView.render();

Please help me out or point me to the right direction would be greatly appreciated.


Solution

  • Since you have an item view, it's better to give it the functionality for removing itself. This way you don't have to hack your way reading stuff from DOM to find the related model based on the clicked item view element.

    Also, you should use the remove() method of Backbone.View rather than the jQuery remove() method, because it safely removes the backbone events registered on the view, as well as calls jQuery remove() to remove the element from DOM.

    You can call model.destroy() which will signal your persistence layer to remove the model and removes it from collection as well. Since you don't have a persistence layer in this example, I'm triggering a custom event on the item view's model which is handled in the collection for removing the model from it (model events propagates to it's collection).

    There is no need to manually initialize models in a collection by yourself, backbone does it automatically, that's what the collections model property is for.

    You should use some sort of templating engine rather than doing string manipulation in the view for rendering, you have _.template() at your disposal anyway.

    Also, as dskoda1 already mentioned, you shouldn't pass a Backbone.Collection using the model option, model and collection are two options that will be detected by Backbone.view. Even if it does no harm, it's still very confusing.

    var Vehicle = Backbone.Model.extend();
    var Vehicles = Backbone.Collection.extend({
      model: Vehicle,
      initialize: function() {
        this.on('delete', this.remove);
      }
    });
    var VehicleView = Backbone.View.extend({
      tagName: 'li',
      className: 'vehicle',
      template: _.template($('#vehicle-template').html()),
      events: {
        "click .delete-btn": "vehicleRemove"
      },
      initialize: function() {
        this.render();
      },
      render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
      },
      vehicleRemove: function(vehicle) {
        this.remove();
        //this.model.destroy(); /* The collection should have a url */
        this.model.trigger('delete', this.model);
      },
    });
    var VehiclesView = Backbone.View.extend({
      tagName: "ul",
      initialize: function() {
        this.render();
      },
      render: function() {
        this.collection.each(function(vehicle) {
          var vehicleView = new VehicleView({
            model: vehicle
          });
          this.$el.append(vehicleView.$el);
        }, this)
      }
    });
    
    var vehicles = new Vehicles([{
      id: 1,
      title: "Toyota",
      regiNum: "453454624"
    }, {
      id: 2,
      title: "Honda",
      regiNum: "daf4526"
    }, {
      id: 3,
      title: "Audi",
      regiNum: "jlkjfa34"
    }]);
    
    var vehiclesView = new VehiclesView({
      el: "#container",
      collection: vehicles
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>
    <div id="container"></div>
    <script type="text/template" id="vehicle-template">
      <%=title%>Registration Number is:
        <%=regiNum%>
          <button class='delete-btn'>Delete</button>
    </script>