0

I'm a little confused as to why my event is not firing on a child view. I usually set a $el attribute but in this instance I am reusing the child view multiple times so I'm setting the className attribute but it's still not firing.

Here's my code:

Child view (simplified):

var LogbookEntryView = Backbone.View.extend({

    className: 'log-entry',
    collection: Logbook,
    template: _.template($('#logbook-entry-view').html()),

    events: {
        "click .edit-log": "editLog",
        "click .popup": "modalHandler"
    },

    render: function(model) {
        return this.template(model);
    },

    editLog: function(event) {
        var target = $(event.currentTarget);
        var id = $(target).data('id');
        var url = target.attr("href");
        var modal = $(target).data('modal');
        this.loadModal(modal, url);
        return false;
    }
})

Parent view method:

displaySkillsByCat: function() {
    var entryView = new LogbookEntryView();
    _.each(_.keys(app.data), function(cat) {
        $("#logbook-tabs ." + cat).removeClass('disabled');
        $("#" + cat).append(app.template);
        _.each(app.data[cat], function(item) {
            $("#" + cat + " .logbook-list").append(entryView.render(item.attributes));
        });
    })

    return this;
},
    groupSkillsByCategory: function(){
  var cats = _.uniq( this.collection.pluck('cat_group') );
  _.each(cats,  function(cat, index){
    app.data['cat' + cat] = Logbook.where({'cat_group': cat});
  });
},
KoalaKid
  • 232
  • 2
  • 11

1 Answers1

1

The problem is that you say you're reusing a view, but in practice, you're not using it at all.

You're just passing data through the render function which returns a string, without any events or anything related to Backbone.

So the first step is to fix the child view class.

  • collection property on a view isn't meant to be used that way, so remove that.
  • A view should fill its el or $el (jQuery equivalent) with a template.
  • render should return this as a standard.
var LogbookEntryView = Backbone.View.extend({
    className: 'log-entry',
    template: _.template($('#logbook-entry-view').html()),

    events: {
        "click .edit-log": "editLog",
        "click .popup": "modalHandler"
    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },

    editLog: function(event) {
        var $target = $(event.currentTarget);
        var id = event.currentTarget.id;
        var url = $target.attr("href");
        var modal = $target.data('modal');
        this.loadModal(modal, url);
        return false;
    },
    modalHandler: function() {},
    loadModal: function() {}
});

Then, in the parent view, you could simplify it a bit.

  • Use _.each directly, you do not need _.keys. If it's a Backbone collection, you could use app.data.each(...).
  • Avoid using the core jQuery function in a loop. Instead, cache the object in a variable, then use it in the loop.
  • Instead of appending the string of the sub view, create a new view and append its el DOMElement.
displaySkillsByCat: function() {
    var entryView = new LogbookEntryView();
    _.each(app.data, function(entries, cat) {
        $("#logbook-tabs ." + cat).removeClass('disabled');
        $("#" + cat).append(app.template);

        // select this element once instead of each iteration
        var $list = $("#" + cat + " .logbook-list");
        _.each(entries, function(item) {

            // here, use `.el` to get the sub-view's element
            $list.append(new LogbookEntryView({ model: item }).render().el);
        });
    })

    return this;
},

The above is a simple fix for your situation, but if you don't keep track of the sub views and they are listening to a Backbone model, there could be memory leaks.

Here's a more efficient way to render a list with Backbone.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
  • 1
    First off thanks for the really detailed response, this was really helpful. It makes complete sense now, why my events weren't firing. Secondly thanks for the tips and link on best practice, I'm new to Backbone this year and best practice is something am struggling with. – KoalaKid Jun 22 '17 at 22:35
  • I do have a couple of comments, re the code. For some reason _.each(app.data) just returns undefined, that's why i was using_.keys(). I've updated my initial question to add the code for the where app.data is being populated (groupSkillsByCategory()), maybe you can see why a standard _.each look is failing. – KoalaKid Jun 22 '17 at 22:39
  • Also now the child view is creating a wrapper div with a className of "log-entry" around my actually template. To counter this I've updated my child view adding a tagName ="tr" and removing from my actual template. However now its not rendering the actual parsed HTML, its just created an empty tr with that className. – KoalaKid Jun 22 '17 at 22:48