Overwriting initialize() in an existing class in Prototype

Recently I’ve been wondering how to overwrite the initialize() method in a class created using Prototype’s Class.create. I wanted to make multiple requests using the same Ajax.Request instance and block it from making the request automatically after it had been instantiated.

Diving in

I wanted to modify the initialize method on Ajax.Request, but did not want to modify the contents of prototype.js. The main problem was the $super argument, which is an automatic reference to the method in the parent class. Prototype documentation (http://www.prototypejs.org/learn/class-inheritance) explains it well:

When you override a method in a subclass, but still want to be able to call the original method, you will need a reference to it. You can obtain that reference by defining those methods with an extra argument in the front: $super. Prototype will detect this and make the overridden method available to you through that argument.

It is used when calling Class.create. However, it is possible to call addMethods() (which Class.create also uses) on the class we want to modify with a hash of properties that will overwrite the original methods. Consider the following example:

Ajax.Request.addMethods({
  // Overwrite the constructor, adding the autoStart option
  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.original_url = url;
    if (options.autoStart !== false) this.request();
  },
  // Store the original request()
  original_request: Ajax.Request.prototype.request,
  // Allow the request to be called without the
  // argument, defaulting to the url sent
  // during initialization
  // Also, allow multiple requests from the same Ajax.Request object
  request: function(url) {
    this._complete = false;
    if (!url) url = this.original_url;
    this.original_request(url);
  }
});

What this change does is introducing a new option to the Ajax.Request constructor (autoStart) which allows us to create a new Ajax.Request object without actually calling the request. We can do it afterwards with the request() function. We can also make the request call multiple times using the same Ajax.Request instance.

Closer look

Let’s look into some of the details:

Ajax.Request.addMethods({

addMethods() is used by Class.create to add methods to the new class, but here we use it to overwrite previous ones.

// Overwrite the constructor, adding the autoStart option
initialize: function($super, url, options) {
  $super(options);
  this.transport = Ajax.getTransport();
  this.original_url = url;
  if (options.autoStart !== false) this.request();
},

This is basically a copy of the original initialize(), but it introduces the key element that prevents the request from being called automatically after the class has been instantiated. Also, in this way we can call $super with no problem as addMethods() will take care of it for us. You’ll also notice that we store the url in a original_url, which will become more obvious in the next part.

// Store the original request()
original_request: Ajax.Request.prototype.request,
// Allow the request to be called without the argument,
// defaulting to the url sent
// during initialization
// Also, allow multiple requests from
// the same Ajax.Request object
request: function(url) {
  this._complete = false;
  if (!url) url = this.original_url;
  this.original_request(url);
}

In this part we create an alias for the original request() method and set the new request() to work as a wrapper around the original, allowing it to be called without arguments and fall back to the original_url set in the new initialize() if it needs to.

Now, when we create a new instance of Ajax.Request, the request won’t be made automatically. Consider this example:

var req = new Ajax.Request('ajax.txt', {
  autoStart: false,
  onComplete: function(transport) {
    alert(transport.responseText + " " + Math.random());
   }
});

This won’t make the request yet, we need to call this to make it work:

req.request()

This will display an alert with the response body and a random number. If you call req.request() multiple time you’ll notice that the url is re-requested and the random number changes.

Good luck!

I hope this helps you hack Prototype without the need of editing prototype.js :)

This entry was posted in Experience, Tutorials. Bookmark the permalink.

Tell us what you think!

We will keep your email private. And you know what the little * means.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>