Jammastergoat - dojo.hitch
by Peter Higgins

This cookie has been a long time coming. I don’t think any one component of Dojo base has more uses, or is used more often. Unfortunately, because of the power contained within this single simple function, it’s terribly difficult to explain, though I am going to take a very noble stab at giving all of you the power contained within the most magic of magic functions: dojo.hitch.

The summary of dojo.hitch says it all, though leaves much unexplained:

Returns a function that will only ever execute in the a given scope. This allows for easy use of object member functions in callbacks and other places in which the “this” keyword may otherwise not reference the expected scope. Any number of default positional arguments may be passed as parameters beyond “method”. Each of these values will be used to “placehold” (similar to curry) for the hitched function.

Lets focus on the first part: “Returns a function that will only ever execute in a given scope”. Scope being the first argument passed, method being the second, and though not clearly stated above: any number of positional arguments to pass to the new function (which is suuuuuper cool, and super helpful).

so dojo.hitch() returns a function. The basic training needed to visual this is:

     var foo = dojo.hitch(scope,method);
     foo();

Again, it’s a difficult concept to explain without a real use case, so lets invent a simple one:

var foo = {
     attr:"an attribute",
     debug: function(){
          console.log(this.attr);
     }
};
someNode.onclick = dojo.hitch(foo,"debug");

So when we click on the node referenced as ’someNode”, the debug() method of the foo object will be executed in the scope of foo. ‘this’ means something useful. Here, this.attr is foo.attr, as we’re in the scope of ‘foo’ dictated by hitch. neat.

Stepping back, here is an entirely redundant use of dojo.hitch():

dojo.hitch(dojo,"style","someNode","display","none")();
// is the same as:
dojo.style("someNode","display","none");
// and
var foo = dojo.hitch(dojo,"style","someNode","display","none");
setTimeout(foo,2000);
// except foo gets executed after 2 seconds. a shorthand:
var randomOptional = "block";
setTimeout(dojo.hitch(dojo,"style","someNode","display",randomOptional),1000);
// this time, one second. but pass a random var from this scope 'randomOptional'

An even better, though lengthier example:

var collection = {
    otherFunc:function(e){
        console.log("me first");
    },
    myFunc:function(e){
        this.otherFunc(e);
        console.log("I did something",this,e);
    }
};
var node = dojo.byId("foo");
dojo.connect(node,"onclick",dojo.hitch(collection,"myFunc");

I say hitch() is magic because it happens in a lot of cool places you’d want it to and you probably just never noticed. The above dojo.connect call can simply be written as:

dojo.connect(node,"onclick",collection,"myFunc");

This works wonderfully, and exactly the same as the above hitch()’d version, though only does as dojo.connect does and pass the event object to ‘myFunc’. This is where the “optional positioned arguments” come into play. dojo.hitch() gives us the power to pass arguments from a different scope into the the function on the fly:

// a global variable
var outside = null, outsideWithout = null;
(function(){
        // hidden within this closure
	var thinger = {
		attr:"somestring",
		debug: function(){
			console.log('attr',this.attr);
		},
		setAttr:function(str,opt){
                        // inspect this.attr before and after
			this.debug();
			this.attr = str + (opt.why || "");
			this.debug();
		}
	};
        var randomString = "because";
        // create a hitch() within this scope linking outside
	outside = dojo.hitch(thinger,"setAttr",randomString);		
        outsideWithout = dojo.hitch(thinger,"setAttr");
})();
// it worked, it's a function, call it to set a new attr value
if(dojo.isFunction(outside)){
	outside("a new attr");
        outsideWithout("lacks the phase 'because' in the text");
}

Above, we created a variable ‘thinger’ which is otherwise entirely private as far as JavaScript will allow. The only available link to thinger, is the ‘outside’ function, hitch()’d exclusively to the setAttr method. It executes only ever in that scope, and has access to those values. Nothing else does.

This is great, and slightly confusing I’m sure. I’ve still yet to produce a _real_ usecase. Onward, lets make a simple declared class based on Nikolai’s custom connect example:

dojo.provide("my.Thinger");
dojo.declare("my.Thinger",null,{
      constructor:function(args){
          console.log(args);
      },
      show:function(){ },
      hide:function(){ }
});

It doesn’t do anything, but is generally how all dijit’s and declared classes work. (I’m inching towards use case, bear with me).

var one = new my.Thinger("firstone");
var two = new my.Thinger("secondone");
var obj = {
     hideFirst: function(){
          one.hide();
     }
}
dojo.connect(two,"show",obj,"hideFirst");

Here, hitch() is quietly used by dojo.connect to run obj.hideFirst() anytime the instance two.show() is called.

The most common use case for hitch (as far as my experience has taken me) is passing ‘this’ as the first argument (the scope), to call a function that otherwise would not be called in any particular scope.

var randomObj = { 
     errorFound:function(data,ioargs){
        // my generic error handler
     }
};
dojo.xhrGet({
    url:"script.php",
    load:function(data,ioArgs){
       // loaded okay, do something with 'data'
    },
    // there was an error, pass off to my generic error handler
    error: dojo.hitch(randomObj,"errorFound")
});

Its important to note that hitch() returns a function rather than executing it. dojo.addOnLoad(function(){ }) is not the same as dojo.addOnLoad(someFunction()) … (someFunction() in this example executing immediatly, and the anonymous actual function just being a reference (a scoped area, at that) which is called by addOnLoad. Without looking, I would bet addOnLoad uses hitch() somewhere along the lines
of it’s magic.

A semi-real-world use case: When a fadeOut is done, simply tell some other methd to fire, say disable a dijit.form.Button with an id=”myButton”:

var button = dijit.byId("myButton");
dojo.fadeOut({
    node:"someDiv",
    onEnd: dojo.hitch(button,"setAttribute","disabled","true")
});

or with other things that don’t fire synchronously, like most Ajax calls:

var thinger = function(node,url){
		var node = null;
		var load = function(someurl){
			dojo.xhrGet({
				url: someurl,
				// pass 'data,ioArgs' to setContent via magic, er, hitch()
				load:dojo.hitch(this,"setContent"),
				error:function(e){
					console.warn('oh noes',e);
				}
			});
		};
		var setContent = function(data){
			node.innerHTML = data || "";
		};
		node = dojo.byId(node);
		load(url);
	}
	// set div id="someNode" to the result of foobar.html
    var yep = new thinger("someNode","foobar.html");

Which is about as far as I can take hitch() in a cookie. Note, my thinger class to set the result of a node byId() to some url is not near as elegant a solution as pottedmeat’s universal fix, but does help to illustrate the magic of dojo.hitch().

One last item where hitch() is magically used to set the scope of something: forEach. The optional third parameter is used to scope the function in the second paramter somwhere. Again, ‘this’ is a very common usage:

dojo.declare("thinger",null,{
	constructor:function(){
		this.letters = [];
		dojo.forEach(["a","b","c","d"],function(item){
			// 'this' in this context means our local scope
			this.addLetter(item);
		},this);
	},
    addLetter:function(letter){
		this.letters.push(letter);
	}
});
var foo = new thinger();
console.log(foo.letters);

Go hitch(). I recall at TAE 2007, during a prototype talk someone referring to dojo.hitch() as “romantic” (and it kind of is in a sense) though was mistakenly comparing it to prototype’s .bind(), which is the same as the automatic relationship between dojo.connect() and dojo.hitch(), though hitch() is endlessly magic, and needs no romance to be useful. :)

Buen provecho.

Tags: , , ,

This entry was posted on Saturday, March 15th, 2008 at 2:37 pm and is filed under Dojo Cookies. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

10 Responses to “Jammastergoat - dojo.hitch”

  1. toonetown Says:

    One question I’ve always had about hitch is a bit more explaination on the “positional” arguments…for example:

    var foo = {attr:”an attribute”, debug: function(v){console.log(this.attr + ” - ” + v);}};
    var val = “Me”;

    dojo.hitch(foo, “debug”, val)(”Him”);

    What will be produced? “an attribute - Me” or “an attribute - Him” ?

    Does that make sense? Does positional apply to when it’s called or when it’s defined?

  2. pottedmeat Says:

    @toonetown, Hitch puts the hitched parameters at the beginning, and the new arguments at the end.

    In the example above, since you’re only using the first argument, it would log “Me”. But in the second argument, you’d have your passed value of “Him”.

  3. toonetown Says:

    Ok - so (as a reference for me in the future), the following code:

    var foo = {attr:”an attribute”, debug: function(v, v2){console.log(this.attr + ” - ” + v + ” - ” + v2);}};
    var val = “Me”;

    dojo.hitch(foo, “debug”, val)(”Him”);

    Will produce “an attribute - Me - Him”

    Great!

  4. pottedmeat Says:

    Correct!

  5. rcoup Says:

    dojo.hitch() is also fantastic for interacting with third party APIs. Since nobody wants nasty global-namespaced functions, we can use hitch to tidy it up. Here’s a simple Google Maps example, since the GEvent functions don’t support any scoping.

    dojo.provide(”my.maps”);

    dojo.mixin(my.maps, {
    savedPoint: null,

    onMapClick: function(overlay, point) {
    // since we used dojo.hitch() this is called with this==my.maps, which is what we want
    this.updateSavedPoint(point);
    },
    updateSavedPoint: function(point) {
    this.savedPoint = point;
    }
    });

    // connect our GMaps event listener, and use the my.maps scope for our handler
    var eh = GEvent.addListener(gmap, “click”, dojo.hitch(my.maps, “onMapClick”));

  6. Continuing Intermittent Incoherency Says:

    [...] Higgins has an excellent article up that covers dojo.hitch(), and in general the Dojo Cookies series over at dojocampus.org is rocking [...]

  7. occident.us » Blog Archive » Dojo Cookies Make Curry Says:

    [...] set of tutorials over at dojocampus.org cutely dubbed “cookies.” Reading through the article Alex highlights on dojo.hitch I even learned something new. Turns out dojo.hitch isn’t just a nice tool for [...]

  8. DojoCampus » Blog Archive » Demystifying dojo.data Says:

    [...] absolutely love the way that it simplifies my life. While it’s not quite as “magic” as dojo.hitch(), things “just work” when I use it. Many of the dijit widgets support being tied to data stores. The [...]

  9. DojoCampus » Blog Archive » Dojo and Air, a fancy file uploader Says:

    [...] dojo.hitch method which takes all the heavy load of your shoulders. I recommend reading the cookie by Peter Higgins about dojo.hitch Note the third parameter passed to the dojo.hitch method. All parameters after the second one just [...]

  10. Learning Dojo | SitePen Blog Says:

    [...] (article) Jammastergoat: dojo.hitch [...]

Leave a Reply

You must be logged in to post a comment.