Monday, September 19, 2011

Beginning Namespacing in Backbone.js

‹prev | My Chain | next›

Up tonight, I hope to namespace my Backbone.js app. I have been putting everything in the global namespace:
  $(function() {
    window.Appointment = Backbone.Model.extend({...});

    window.AppointmentList = Backbone.Collection.extend({...});
    window.Appointments = new AppointmentList;

    window.AppointmentView = Backbone.View.extend({...});
    window.AppointmentEditView = Backbone.View.extend({...});
    window.AppointmentAddView = Backbone.View.extend({...});
    window.DayView = Backbone.View.extend({...});
    window.AppView = Backbone.View.extend({...});

    new AppView();
  }); 
Maybe something like this would work better:
    Cal.Models.Appointment = Backbone.Model.extend({...});

    Cal.Collections.AppointmentList = Backbone.Collection.extend({...});
    Cal.Appointments = new AppointmentList();

    Cal.Views.AppointmentView = Backbone.View.extend({...});
    Cal.Views.AppointmentEditView = Backbone.View.extend({...});
    Cal.Views.AppointmentAddView = Backbone.View.extend({...});
    Cal.Views.DayView = Backbone.View.extend({...});
    Cal.Views.AppView = Backbone.View.extend({...});

    Cal.App = new AppView();  
I kinda like the module-like namespacing that this yields. It is not real class namespacing though. Rather it is just building up object literals that could be expressed as:
    var Cal = {
      "Models": {...},
      "Collections": {...},
      "Views": {...}
    }
The problem with fake namespacing is that I then need to use the long form everywhere:
    Cal.Collections.AppointmentList = Backbone.Collection.extend({
      model: Cal.Models.Appointment,
      url: '/appointments',
      parse: function(response) {
        return _(response.rows).map(function(row) { return row.doc ;});
      }
    });
Another issue that I have is putting instances of these classes into the "Cal" namespace. If this were Ruby, it would not make any sense to put the Appointments instance into Cal::Appointments. Cal::Appointments would refer to the class name.

I will keep that in my back pocket. Maybe creating yet another object would be the way to go? Something like the following (Underscore.js's extend()):
    var Cal = function() {
      this.initialize();
    }
    _.extend(Cal.prototype, {
      initialize: function () {
        this.appointments = new this.collections.AppointmentList;
      }
      models: {
        Appointment = Backbone.Model.extend({...})
      },
      collections: {
        AppointmentList = Backbone.Collection.extend({
            model: this.models.Appointment,
            url: '/appointments',
            parse: function(response) {...}
        });
      },
      views: {...}
    };
That seems awfully Javascripty and more than a little obscure. At this point, I still don't think I have a good solution, and I am starting to flail so I will call it a night. Despite being somewhat tough to read, I think the object approach may yield some prettier code in the end. Then again, that is probably just fatigue talking -- all the more reason to call it a night here and pick back up tomorrow.

Day #138

1 comment:

  1. Regarding "having to reference the long form everywhere": there usually aren't many places you have to reference them, because objects don't call each other very often (and it's usually something to avoid doing except in the necessary cases).

    Also, maybe there's an interesting way to do this with function.call?

    So once you setup Cal as a function, in a separate part (outside the definition of Cal), something like:

    Cal.call(Cal, function() {
    this.Models.Event = Backbone.Model.extend({
    // can we bind the scope in here such that a
    // call to Models.Day hits Cal.Models.Day?
    })
    }

    ReplyDelete