Initialize a mixin in Backbone

Posted June 29th, 2014 with 2 Comments

Sometimes is useful to extend a class with a mixin rather than keeping deriving a subclass to add functionalities (Mixin). A mixin could be useful also to keep your class thin.
In Backbone this is quite easy

MyApp.Models.MyClass = Backbone.Model.extend({
  myMethod: function() {
    // do something
  }
});
MyApp.Mixins.MyMixin = {
  myMixinMethod: function() {
    return this.myMixinProperty;
  },
  myMixinProperty: 42
};

// extend the original class with the mixin
_.extend(MyApp.Models.MyClass.prototype, MyApp.Mixins.MyMixin);

// now my class has the new methods
var myClass = new MyApp.Models.MyClass();
myClass.myMixinMethod(); // 42

Then it may happens that we need to initialise the mixin, for example to setup some variables (in this example the initial value of the property is a simple number but this could lead to some problem in case the property is a variable passed by reference). The temptation is to call the initialize method of the mixin right in the extended class

MyApp.Mixins.MyMixin = {
  initializeMyMixin: function() {
    this.myMixinProperty = 42;
  },
  myMixinMethod: function() {
    return this.myMixinProperty;
  },
  myMixinProperty: null
};
MyApp.Models.MyClass = Backbone.Model.extend({
  initialize: function() {
    this.initializeMyMixin();
  }
});

But this is not a good practice since the extended class now depends on the mixin class. One big advantage of the mixin is that we can add the some methods to different classes without having to derive a subclass from each of them, a kind of simple multiple inheritance.
This works if the mixin is loosely coupled and my general rule of thumb is “if a method refers to something in the mixin, it must be in the mixin”.
We can solve it with a little bit of conventions and meta programming

MyApp.Models.MyClass = Backbone.Model.extend({
  initialize: function()
    var _this = this;
    _(_.functions(this)).each(function(func) {
      if (func.match(/^initialize[A-Za-z0-9]+$/)) {
        _this[func].apply(_this, arguments);
      }
    });
  },
  myMethod: function() {
    // do something
  }
});

Basically the initialize method of the Backbone class to be extended searches for methods like initialize* and execute them. With this convention we saved our loosely coupling (if we remove, add or change our mixin, nothing changes in the class to be extended).

Deferred object as callback

Posted October 10th, 2012 with 6 Comments

Callbacks are a nice way of Iversion of control, but are even more powerful with deferred objects.
Basically callbacks are a way to pass back the control from a called function, the deferred/promise just do the opposite: it returns the control the called function.

Consider this scenario: a table widget, with the classic delete/modify buttons next to each row. Since the table widget is a generic component, we want to handle the delete operation on the model outside the widget, and we don’t event want to extend the generic object to do that, we want to user a callback:

$.widget('my_table_widget',{
  onDelete: function(id_of_row) {
    var that = $(this);
    // call the backend and delete the record
    $.ajax({
      method: 'delete',
      url: '/api/records/'+id_of_row
      })
      .done(function() {
        // ok, record delete, now I should update the table
        that.widget('remove_row',id_of_row);
        });
      }
    });

In the example above: the callback first calls the backend to remove the record and then removes the row from the table (I mean the DOM). In order to do this: it needs to know something about the table widget (in this case, the method “remove_row”).
But what if we could pass the ball back to the widget? Let’s rewrite it with a deferred object:

$.widget('my_table_widget',{
  onDelete: function(id_of_row) {
    var deferred = $.Deferred();
    // call the backend and delete the record
    if (confirm('Delete?')) {
      $.ajax({
        method: 'delete',
        url: '/api/records/'+id_of_row
        })
        .done(function() {
          // ok, record delete, now I should update the table
          deferred.resolve();
          })
        .fail(function() {
          deferred.reject();
          })
      }
    else {
      deferred.reject();
      }
    return deferred.promise();
    }
  });

It’s more linear but, most important, the callback knows nothing about the remove_that_damn_row_from_the_dom method of the widget, it just passed back the control, it’s like saying “I’m done, it’s your turn”.
More separation, less documentation to read, easier to implement, less errors.

On the widget side, the callback should be treated this way

// somewhere inside the table widget, here is where we execute the callback
var callback_result = options.onDelete.call(widget,id);
// if the callback answer with a deferred/promise, this will
// handle it when it's resolved/rejected
if (isPromise(callback_result)) {
  callback_result
    .done(function() {
      // ok, asynch operation on the other side is over
      // remove the row from the dome
      widget.widget('remove_row',id);
      })
    .fail(function() {
      // do nothing
      })
  }

Event broadcast in jQuery

Posted June 27th, 2012 with 4 Comments

Eventbroadcast it’s a small library to improve the functionality of bind()/trigger() methods.
This plugin extends jQuery with the method $.trigger(‘‘) which calls the event named <event_name> on all elements previously registered to the event through .bind() and .one().

The main problem with the classic jQuery $(‘‘).trigger() method is that must be used on the element that receives the events, leaving Publish/Subscribe pattern uncomplete: we need to programmatically know the elements to cast the event to.

With the Publish/Subscribe pattern it’s easy to decouple the code in charge to create the event from the code that receives it: the event emitter doesn’t know (and doesn’t care), at runtime, who is listening to the event (speaking with the words of academics, it’s not aware of the topology of the elements).
The more the code is decoupled, the more is maintanable.

Continue Reading

  • Categories

  • Tags

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2 other subscribers

  • Follow me on Twitter