Sunday, September 18, 2011

DRYing up Backbone Code

‹prev | My Chain | next›

I have my Backbone.js app in kinda-OK shape after the past few days of cleanup. This is mostly thanks to some pretty decent test coverage courtesy of jasmine:
I would like to take some time to clean up some minor code annoyances. I start with the reset and add functions bound to the calendar appointments collection:
    Appointments.bind('add', function(appointment) {
      var view = new AppointmentView({model: appointment});
      view.render();
    });

    Appointments.bind('reset', function(appointments) {
      appointments.each(function(appointment) {
        var view = new AppointmentView({model: appointment});
        view.render();
      });
    });
If nothing else, I can DRY that up pulling the appointment View instantiation and rendering into a separate function. While I am at it, I can pull the iteration through the appointment list into a separate function as well. This keeps the bound functions at the same level of abstraction. Or, as I like to think of it, reading nicer:
    Appointments.bind('add', render_appointment);
    Appointments.bind('reset', render_appointment_list);

    function render_appointment(appointment) {
      var view = new AppointmentView({model: appointment});
      view.render();
    }

    function render_appointment_list(list) {
      list.each(render_appointment);
    }
Hrm... having two similarly named functions hanging about makes me want to put them into a single class. But what kind of class? A vanilla Javascript class? A Backbone view? Stumped as to the best approach I give a Backbone view a shot:
    window.AppView = Backbone.View.extend({
      initialize: function(options) {
        Appointments.bind('add', _.bind(this.render_appointment, this));
        Appointments.bind('reset', _.bind(this.render_appointment_list, this));

        Appointments.fetch();

        $('#calendar td').each(function() {
          new DayView({el: this});
        });
      },

      render_appointment: function(appointment) {
        var view = new AppointmentView({model: appointment});
        view.render();
      },

      render_appointment_list: function(list) {
        list.each(this.render_appointment);
      }
    });

    new AppView();
I really do not know if I like that. I have named it "AppView", which is the name of a class in the sample TODO app. To me, this seems like an invitation to pull in a bunch of unrelated crap. Case in point, I have dragged in the assignment of the day Views as well as fetching the appointments collections. Really, I just grabbed anything not already encapsulated in a class because there was so little of it and because this a whole "app" view.

Also troublesome is that this is not a Backbone view—it is not rendering anything directly. On the other hand, it is responsible for rendering a "collection view". It is the parent to all appointment views. As such it is responsible for binding render functions to collections events (reset and add).

Then again, needing to use the underscore.js bind() function feels oogy.

Bah! Maybe I am simply over thinking this. I am DRYing evented Javascript code here. Perhaps my original two functions was the proper way of doing this.

Unsure of which approach I like best, I call it a night here. Either way my tests are still passing. First thing tomorrow I will pick whichever approach I like best and then move on to other concerns.


Day #137

No comments:

Post a Comment