Making sense of dojo.when: A simple XHR caching example

Posted

Right before Dojo 1.5 came out, the Sitepen blog had a great post about the improvements 1.5 would bring for dojo.Deferred. The part that really caught my eye was dojo.when, a method that lets you pass a value to a function whether that value is available now or as the result of some asynchronous operation. Either way, you get a “promise” that when the value is available, the function you provided will run.

This is one of those things that was super-neat when I read about it, but it took me a while to incorporate it into my code — it’s only in the last couple of weeks that I’ve had that wonderful moment when I’ve said “oh, I could totally use dojo.when for that!” Moments like these make me very happy.

It’s pretty common that an application makes an Ajax request for some data, and then caches that data so the request won’t have to happen again; the pattern might look something like this:

var myCache = {};

function getSomeStuff(stuffId) {
  if (myCache[stuffId]) {
    handleResponse(myCache[stuffId]);
    return;
  }

  dojo.xhrGet({
    url : 'foo.php',
    content : { id : stuffId },
    load : function(response) {
      myCache[stuffId] = response;
      handleResponse(response);
    }
  });
}

Here we have a function that takes an ID; the function looks in the cache to see if there’s a value stored for the ID, and if so, it passes the stored value to a handleResponse function. If not, it does an XHR to get the data; when the XHR succeeds, it stores the data in the cache and, again, passes the value to the handleResponse function.

There’s nothing strictly wrong with this, but I discovered that some neat abstraction opportunities became more clear when I switched to using dojo.when instead:

var myCache = {};

function getSomeStuff(stuffId) {
  dojo.when(
    myCache[stuffId] || dojo.xhrGet({
      url : 'foo.php',
      content : { id : stuffId },
      load : function(response) {
        myCache[stuffId] = response;
      }
    }),
    handleResponse
  );
}

Now we’re telling our getSomeStuff function to look for a cached value; if it finds one, dojo.when will immediately pass that value to the handleResponse function. If it doesn’t find one, it will run the XHR, and dojo.when will magically pass the XHR’s response to the handleResponse function instead. This is hot.

This works because dojo.xhrGet returns a “promise” object with a then method. dojo.when looks to see whether it got a promise object as its first argument; if so, it uses the then method of the promise object to attach the callback provided as the second argument to dojo.when. If not, it simply calls the callback immediately on the first argument. The real magic is actually in dojo.Deferred, not in dojo.when itself. Since all of Dojo’s XHR methods return a dojo.Deferred promise, dojo.when will “just work.”

I found that I was going through my application and ripping out instances of the old code, replacing it with the new. And then I had that “oh sh*t I’m copying and pasting, aren’t I …” moment, and saw my way to an abstraction.

In my application, I was actually caching the responses using the URL from which I’d requested them, which works out to be a perfectly unique ID for the data. (This particular part may or may not work in your application.) My abstraction was an essentially drop-in replacement for dojo.xhrGet calls:

var cache = {};

function cacheableXhrGet(settings) {
  var url = settings.url,
      req = cache[url] || 
            dojo.xhrGet(dojo.mixin({
              // override the load handler
              load : function(resp) {
                cache[url] = resp;
              }
            }, settings));

  dojo.when(req, settings.load);
  return req;
}

I can pass a settings object to cacheableXhrGet that looks exactly like the object I’d pass to dojo.xhrGet, but replace the load function before actually passing it to dojo.xhrGet. But before the XHR even has a chance to get set up, I check my cache for a stored response; if I find one, I store it in the req variable, but otherwise I store the XHR there.

In either case, the function defined at settings.load gets the proper response value via dojo.when. For bonus points, I then return either the cached value or the XHR — which means other code can use the return value of cacheableXhrGet for its own dojo.when. How neat is that?

Conclusion

Promises and deferred’s are a really pleasant tool to have in your JavaScript arsenal once you get the hang of them, and dojo.when seems like a great place to start understanding them.

Out of the box, Dojo makes use of deferreds for all of its XHR functionality, meaning that you can pass around the return value of any Dojo XHR method and do fun things you can’t do with jQuery’s $.ajax, like add more callbacks to a request after you’ve set it up.

I’ve just recently started realizing when I could incorporate dojo.Deferred functionality into my own code — again, now that I’ve got the hang of it, I’m pretty sure it’s going to dramatically change how I write asynchronous code.

Disclaimer: This post contains sample code for illustration purposes. In reality it’s all namespaced and these naked functions are actually methods in classes and stuff, and the real code doesn’t even look much like the code you see here. I’ve also completely ignored questions of when to clear or invalidate the cache. You’ve been warned.

Filed under  //  deferreds   dojo   dojo.when   promises  
Comments (4)
Posted

EnterpriseDojo.com uses jQuery!

Posted

And you should too ... for your WordPress blog that came with jQuery already installed and probably barely even needs JavaScript in the first place. 

Here's an idea: Let's have an honest discussion about when a given solution makes sense and when it doesn't, rather than mocking a contributor to that discussion for choosing a perfectly appropriate tool for a perfectly mundane task. EnterpriseDojo.com is a blog about using Dojo in the enterprise, not a blog about using Dojo on blogs. Snickering at its use of jQuery is approximately as useful as pointing to this graph and saying case closed, leaving more reasonable, thoughtful people to explain all the ways that graph doesn't tell us a damn thing

I want to be very clear, as the dust settles around my several recent rants: jQuery has its place, and it is a very, very big place. It is a lovely DOM, Ajax, and events library, and a great way to get certain things done quickly, especially for people who may not have the luxury of learning the inner workings of JavaScript (noble a goal as that is). There was a time when DOM and Ajax and events questions felt like the questions of the day, and jQuery showed us how to answer those questions using a simple, easy-to-learn API that suddenly made JavaScript accessible to the masses. Other libraries solved the same questions but those solutions felt ugly and clunky and goshdarnit, hard, and I give jQuery the utmost credit for making JavaScript suck so much less when all I wanted to do was show or hide or slide a thing or load some content onto my page. I'll even grant that it can be used as a piece of a large application solution, though I question the wisdom of doing so for reasons outlined in another post. Heck, I'll even grant that "enterprises" aren't off their rockers to use it -- for the things it was meant to be used for. 

What upsets me is when smart people seem to say that jQuery's victory in an internet-wide popularity contest suggests, well, anything at all when it comes to more complex needs. It upsets me because my clients hear those suggestions, look at that same well-marketed graph, and I am left explaining to them that, yes, jQuery is popular, but it's popular because it answers a small set of questions easily and well -- even for people who don't even know JavaScript! -- and you, dear client, have vastly larger questions than that.

At the end of the day, these toolkit decisions ought to be about more than a popularity contest; jQuery may be the right answer, or part of the right answer, but it's imperative, to me, that my clients understand the scope of the question first. It's imperative that they make their decisions based on a full understanding of pros and cons, risk and reward, cost and benefit -- not based on a graph, not based on a popularity contest.

So again I say: we, as a community, and especially the influential ones among us, do well when we elevate the conversation beyond that contest and acknowledge that choosing the right toolkit depends on first understanding what you're choosing the toolkit for. We do well when we educate teams and decisionmakers on the lessons we've learned in the time since DOM, Ajax, and events were the big question of the day, on the best practices that have emerged, on the situations where we've, gasp, had to look beyond jQuery -- either to other tools or other toolkits -- for the answers. And we do well when we start showing them how they can do the same.

An aside: Some people I like a lot have pointed out that in the midst of all of my complaining, I have not come out and offered a solution. This is fair. Two things: One, I have not wanted this conversation to collapse into Dojo vs. jQuery vs. YUI vs. Ext vs. MooTools vs. whatever, because if it does, I think we've missed the crux of the matter: that different tools do different things, that some set out to answer complex questions and some do not. If anyone has been unclear, which I rather doubt, my personal preference of late has been to use Dojo. I do not recommend using Dojo for everything under the sun, but I find it offers a lot of utility when writing non-trivial applications. I cannot make a compelling argument for using it vs. YUI, simply because I don't know YUI well enough. I can't even make a compelling argument for using it vs. Ext, except for licensing issues that may or may not be relevant in a given scenario. Two, if you're disappointed that I haven't offered a solution, especially a jQuery-based one, I apologize. However, I feel there are too many viable existing solutions out there already, and I haven't come up with a good reason to promote a jQuery-based solution besides jQuery's popularity. And, well, see above for my thoughts on that.

Filed under  //  dojo   jquery   large-applications  
Comments (4)
Posted

On Rolling Your Own

Posted

There’s been a lot of activity around my last post, On jQuery & Large Applications. A number of people have asked me why, exactly, I’m so opposed to using jQuery as part of a roll-your-own solution.

To answer that, let’s start by looking at (some of) the pieces my ideal large application solution might include:

  • DOM manipulation tools
  • Ajax tools
  • A dependency management and build system
  • Clear patterns for code organization, such as namespaced modules
  • An inheritance system, preferably one that offers multiple inheritance, for sharing code across modules and staying DRY
  • A non-DOM-centric, loosely coupled API for communication between modules
  • A widget system that makes use of the inheritance system, with lifecycle management (setup/teardown) and templating
  • A system for maintaining templates separate from JavaScript while interning them into the build to eliminate HTTP requests
  • A system for abstracting RESTful server communication
  • For a UI-intensive project, a rich widget system pluggable to arbitrary data sources and designed with an easily-extended API
  • For an enterprise project, a11y and i18n provisions, as well as clear patterns for file organization

To all of you who have said you can do this yourself, you win. I can’t argue with you without sounding like I’m saying you’re too dumb, and you’re probably not.

But here’s my question: why? What, exactly — exactly — do you gain by putting all of this together for yourself, rather than using the pieces you need of a toolkit that provides all of this out of the box? Because here are a few things I think you lose:

  • Integration. Is your abstracted RESTful data API designed to talk to your widget system that’s designed to talk to your template system? That’s hott. Now what about when a new version of one of those components comes out that violates an assumption you made?
  • Maintenance. I heart the good folks who have put together individual answers to these individual questions, but they have no obligation to continue being the good folks they are, and they certainly have no obligation to do it on any sort of schedule. Remember all those plugins that broke with jQuery 1.4? That sure was fun.
  • Documentation. I’m going to grant you, right now, that jQuery is one of the best-documented JavaScript libraries out there, hands down. But what about all these other pieces you’re putting together? Especially the ones you really are rolling on your own, like that templated widget thing that communicates so nicely with your abstract data API. There are a wealth of resources for understanding, troubleshooting, and using these pieces in established toolkits. Where will the next developer turn when they have questions about yours?
  • Experience. Like I said, I get that you’re smart. Possibly smarter than me. That’s cool. But are you smarter than the combined wisdom of a team of people that has been thinking about these questions for years? Are you sure your solution has thought through all the questions they have?

I’ve noticed that, in the conversations I’ve had the last few days, it seems to fall to me to “prove” that a roll-your-own solution that includes jQuery isn’t advisable. Perhaps that’s fair — “you started it!”, you might say, and that I did. But simultaneously, others argue that jQuery never set out to answer these questions, and so it’s not jQuery’s fault that people are trying to use it in ways it wasn’t intended to be used. I have waited in vain to hear a compelling reason why jQuery should be part of a large application solution, to hear why I should recommend a roll-your-own solution that includes jQuery to my clients. The extent of the argument seems to be “because I like it, and it doesn’t force me to think a certain way.”

No one puts baby in a corner. Got it. But the straw man-ness of this argument has me, literally, chuckling right now. Let’s not confuse a mythical one-size-fits-all solution with a toolkit that provides, well, tools. Tools to do all sorts of things, tools meant to work together, tools developed and tested and maintained by a whole big team of smart people, tools that are, actively, being used in really frigging big, really frigging enterprisey applications.

I very purposefully didn’t propose a particular alternate solution in the original post, but it’s hardly a secret that my personal favorite, of late, has been Dojo. Not because it purports to solve every problem or prescribes how to solve them, but because it gives me so many tools to use to solve a given problem. Time and again I find that “Dojo already did that” — they already wrote the tool I’m wishing I had. Now I don’t have to write it, and, perhaps more importantly, I know it was written to work with all of the pieces I’m already using, and when I use it I’m not risking duplication of code or a lack of testing, maintenance or support. Win.

But let’s be very clear: no one’s forcing me to use that component! No one is forcing me to do things a certain way, any more than jQuery is “forcing” me to think of my application entirely in terms of the DOM. I can write my own component if I want, or use someone else’s if I want, or change it a bit if I want! For example, on a current project I pulled in mustache.js because the project had a lot of templates that had already been written to use it. The brilliant thing, though, was that integrating mustache.js into dijit._Templated instead of the standard templating system was trivial. That component, and all the others in Dojo and Dijit, are architected explicitly not to be one-size-fits-all. They provide a rock-solid base for large application development, for getting up and running quickly using a bevy of ready-made solutions, but also provide so many extension points that you can turn those solutions on their head if you want or need.

Garann Means, whose blog you should be reading, took a bit of issue with my original post in model-view-controller and comfy clothes.

I do agree that it benefits everyone to be working in the same setup and making use of tools that have been vetted by geniuses whose entire job is to create such things. But I’m dubious about any approach which comes too close to promising one size fits all. If you’ve ever sewn a dress, you understand that one size fits all is technically possible, but some people are going be left with a lot of excess while others will scarcely be able to breathe.

Carrying on that metaphor, these pieces provided by Dojo — or any other comprehensive toolkit, for that matter — are but starter patterns, and thread, and scissors, and pins, and a sewing machine, and OK I’m stretching the metaphor, now, but my point is they’re definitely not finished one-size-fits-nobody garments. On the other hand, if I decide to use jQuery in a large application, it can feel like I’ve been given a black marker and some of that crinkly brown paper, and now it’s up to me to draw a pattern and then come up with all those other pieces, too. Intellectually interesting and pleasingly crafty, perhaps, but not particularly efficient, sustainable, repeatable, or maintainable.

So again I ask, in all seriousness and in hopes of fostering a good discussion: Why? jQuery provides you with DOM, Ajax, and event management tools, but little else. There are tools designed for building large applications, designed to provide all of the pieces I want and so many more it’s not even funny, and they provide you with DOM, Ajax, and event management tools, too. What’s the compelling case for rolling your own solution that includes jQuery instead?

Filed under  //  dojo   jquery   large-applications  
Comments (20)
Posted

Lessons Learned from Taking On a Project in Crisis

Posted

I just got done with an emergency project for an agency developing a public-facing application for a mutinational technology client you've most certainly heard of. I developed the entire front-end -- HTML, CSS, and JavaScript -- for a non-trivial application with a limited spec in just seven days. The experience was so eye-opening that I feel the need to write down some of the things I've learned, in hopes that I can benefit from my experience in the future.

  • Demand all technical source material up front, such as functional specs, mockups, work that's been done to date, etc. Give the client a fixed amount of time to deliver that source material, and don't make a decision about taking on the project until you've seen it. What the client can deliver in that fixed amount of time will shed a lot of light on the state of the project and whether their expectations are realistic.
  • Set clear time expectations. Am I willing to work 16 hours a day? Am I expected to? Are there hours during which I'll be expected to be available? Am I willing to work on the weekend?
  • Find out whether the client expects me to be available after the imminent deadline, and to what extent. The last thing I want is to snatch defeat from the jaws of victory by being unable to support the code I've written.
  • Do not accept responsibility for commitments made on my behalf. The recruiter said I'd be available six hours a day when I told him four? Not my problem. The client committed to having a feature ready for review without consulting me? They probably won't make that mistake again.
  • Ascertain the rest of the team's commitment to the project. If I'm expected to work long hours, will they be there during those long hours to get me what I need? Are there any constraints on their availability?
  • Establish a single point of contact at the client, and make clear I'll be depending on them to get me any information I need and I'll be treating their decisions as final. Insist that they participate in all calls I'm expected to participate in.
  • Limit the amount of work I do before I have access to all client systems I'll need access to: version control, testing environments, ticketing systems, etc.
  • Insist on a ticketing system. I'm new to the project and I have a lot to get up to speed on. I don't want emails flying at me from all directions -- decisions and technical requirements need to be documented in a single place that everyone can see. This is my only insurance when someone wants to know why something isn't done, or why it wasn't done the way they expected.
  • Insist on version control, even if it's something crappy like CVS. I'll need a way to make sure the rest of the team has access to my latest and greatest. FTP blows, especially when I'm FTPing to a server where another developer is constantly deploying a new build, overwriting my work.

What other advice do you have?

Filed under  //  dojo   freelancing   front-end development   thoughts  
Comments (12)
Posted

Dojo Confessions (Or: How I gave up my jQuery Security Blanket and Lived to Tell the Tale)

Posted

This is a reprint of an article that originally appeared in the October issue of JSMag. I recently had the opportunity to architect the front-end of a new web application from scratch, and after years of using jQuery, I decided to give Dojo a try. For a variety of reasons — not least of which was Dojo’s approach to code organization and dependency management — I thought this would be a good project to get some real-world Dojo experience under my belt. What follows is an overview of Dojo from the perspective of an avid jQuery user. I’ve been using jQuery for years. Its simplicity is seductive; after a while, it kind of writes itself. And maybe that was my problem: I was ready to try something new. Many months ago, I wrote on Twitter that I was interested in learning more about Dojo. Rey Bango, jQuery evangelist, wrote back, and put me in touch with Pete Higgins, the Dojo project lead. Pete proceeded to lobby me for months to give his “unified toolkit” a try. I dabbled. I read the docs. Pete plied me with sample code to show me what I was missing, and even drove to North Carolina to lead a Dojo camp. In August, I decided it was time to stop dabbling and dive in. I’d just finished writing some essentially library-less JavaScript for a web-based iPhone application, a task that left me much more confident in my knowledge of JavaScript. With the mobile site out of the way, the client was ready to build the desktop version, and I would be in charge of the front end. It was time to choose a library; this time, I chose Dojo.

Deciding on Dojo

The decision to try a new library on a client project was a tough one — I knew that I could write the basic functionality of the site using jQuery a whole lot faster than I would write it with Dojo, simply because I knew my way around jQuery so much better. Using Dojo would mean I’d be looking up a lot of things, from simple stuff like how to work with DOM selections, to more complicated tasks like how to structure my code for the purposes of reuse and abstraction. As my work on the project progressed and the deadline neared, I had plenty of second thoughts. A few things convinced me to stick with Dojo when the going got tough:

  • Code organization patterns: Dojo provides pretty clear guidance on structuring both your features and your codebase. I’ve given a lot of thought to organizing jQuery code. I wrote an article on the topic for JSMag and gave a presentation on the topic at the jQuery conference. I was eager to try a library that explicitly answers the organization question.
  • Class inheritance: I knew from the start that I was going to use a lot of interaction patterns over and over. I wanted to be able to write those patterns in a way that would let me use them across features while still staying DRY. The class inheritance provided by dojo.declare() was an elegant, easy-to-use solution to the problem.
  • Dependency management: Being able to easily manage dependencies was a huge draw for me; it promotes reuse and abstraction in a big way. Dojo’s dependency management would also pave the way to easily building production-ready files that would combine all the necessary functionality for a given type of page, reducing the number of HTTP requests required.
  • Service method descriptions: This particular application relied on XHRs (AJAX) in a big way. Dojo’s service method description approach would let me manage the URLs and parameters for those requests in a single place, keeping pieces that might change separate from the core code. Eventually, theoretically, the server-side code could actually generate this SMD file automatically. More on this in a bit.
  • Templating: All the XHR responses were JSON, which I’d need to turn into HTML. jQuery has templating plugins to solve this problem, so this wasn’t really a differentiating factor, but nonetheless it was going to make my life easier. I could maintain the templates for turning JSON into HTML separately from my JavaScript, and even programmatically choose the template depending on the situation.
  • The meaning of this: When binding a function to an event on an element in jQuery, this inside the function refers to the element that triggered the event. This is arguably desirable for simple code, but when you start organizing your code into objects with methods and you want this to refer to the object, not the element, it can get painful. The dojo.hitch() method lets you cleanly change the meaning of this for any given function, and it’s transparently rolled into other methods, such as dojo.connect() for event binding.
  • Documentation and support: Dojo has a reputation for poor documentation, and to some extent it’s deserved. Their documentation is a whole lot harder to use than jQuery’s because, at first glance, it’s quite a bit more scattered and substantially more API-based than task-based. However, once I figured out where to look for the docs I needed, finding answers to my questions was pretty painless. I also leaned heavily on some experienced Dojo developers for guidance and support, and dropped in to the ever-helpful #dojo IRC channel on Freenode if I got stuck.

Getting Started

The first step was assembling my new toy. I opted to use the library via Google’s CDN so I could get up and running as quickly as possible. After that, it was time to figure out how I’d organize my files. Dojo actively supports namespaces for components, which means you can put your application files in one or more directories and associate your namespaces with those directories. I created a high-level controller file in the root /js directory; it would be responsible for figuring out which functionality was required for a given page (a decision I’ll eventually revisit). Then, I created a directory inside the root /js directory, where I’d put all of the individual files for the various components. Because I was using the Google-hosted version of Dojo, I had to put a script tag in my header template, before I included the base dojo.js, telling Dojo where to find my local files:

Finally, I included a line in my controller file to tell Dojo where to find the namespace I’d be using: dojo.registerModulePath('myNamespace', '../js/myNamespace'); Figuring out all of these pieces may have been the hardest part of making the switch to Dojo — it was a whole lot more setup than I was used to with jQuery, and though it is all documented, it took a bit of effort to find the details and to get the paths set up correctly. The time it took to get everything working properly was time that I spent wondering whether I’d made a good decision. Once it was working, it was time to write some actual code and try to answer that question.

Get Some Elements, Do Something With Them

Those words sum up the jQuery paradigm. In jQuery, you query the DOM using a CSS selector, and the result of that query is a jQuery object, which you can then operate on using method chains. It’s fairly rare in jQuery to work directly with a DOM element. While Dojo supports this paradigm through its dojo.query() method and the NodeList it returns, it’s common in Dojo to work directly with a DOM element rather than a NodeList. My initial inclination was to stick with what I knew from jQuery, and to use dojo.query() to get everything I wanted to work with. As I dug in, though, I discovered that it could actually be just as elegant (and less expensive) to work directly with DOM elements, even though they didn’t come with any of the magic of a jQuery object. The syntax for doing so was a bit different — for example, dojo.addClass(myDomElement, ‘foo’) instead of $(myDomElement).addClass(‘foo’) — but the more code I wrote, the more frequently and easily I found myself using the dojo.addClass syntax instead. Embracing this approach was especially valuable when it came to methods that returned something. For example, the dojo.connect() method (used to connect events to elements, similar to $().bind()) returns a connection object, which can be stored and disconnected later without having to know which element the event was attached to. This is, in a word, awesome. It’s also an example of how Dojo requires you to think somewhat differently about how you write your JavaScript.

Returning a Result For the Win

Along those lines, I had to get used to the fact that a lot of Dojo methods returned objects that I could talk to later. For example, dojo.animateProperty() created an animation object which could later be play()’d. All of the XHR methods — and asynchronous methods in general — returned a “deferred” object, to which I could later add success and failure callbacks. jQuery does return the native XHR object from its $.ajax() method, so you can technically add callbacks there too. What I liked about Dojo’s deferred approach is that it provides a common, simple interface for interacting with all asynchronous operations, and even lets you define your own. Getting the hang of how to take advantage of these things took some doing, coming from the more procedural, chained world of jQuery, where just about everything returns a jQuery object. Soon, though, I was setting up animations long before I was ready to play them, and adding callbacks to XHRs after they started.

SMDs: A Unified Way to Talk to the Server

One thing I really wanted to try with Dojo was making use of Service Method Descriptions, or SMDs. An SMD file basically contains information about all of the services provided by a resource. In my case, the resource was the server-side application, which I’d be communicating with to request JSON data. By creating an SMD file, and then instantiating a new Service based on that file, I could create a single place for managing all the paths and parameters I’d use to get what I needed from the server. When I asked the server for something, the Service I created would return a deferred object, to which I could attach callbacks. In the background, Dojo was just running an XHR, but my individual classes didn’t have to worry about the details — I just had to worry about the name of the method and the parameters it required, and the Service I’d defined and instantiated would take care of the rest. For my initial work, I just created the SMD file by hand, but eventually it’s easy to see how the SMD could be generated by the server-side application itself. Here's a sample SMD (normally an SMD would have a lot more services, obviously):

{
        transport        :         'GET',
        envelope         :         'PATH',
        target                 :         '/json',
        
        services         :         {
                callouts : {
                        parameters : [
                                { name : 'callouts', type : 'string' }
                        ],
                        returns : 'object'
                }
        }
}

And here's some abbreviated code that makes use of the SMD:

dojo.provide('myProject.Callouts');

dojo.require('dijit._Widget');
dojo.require('myProject.Services');
dojo.require('dojox.rpc.Service');

dojo.declare('myProject.Callouts', dijit._Widget, {
        cache : {},
        
        services : new dojox.rpc.Service(dojo.moduleUrl('smd', 'json.smd')),
        
        postCreate : function() {
                // ...
        },
        _lookup : function() {
                var val = this.domNode.value;
                
                        if (this.cache[val]) {
                                this._handleResult(this.cache[val]);
                        } else {
                                var c = this.services.json.callouts({
                                        'callouts' : val
                                });
                                
                                c.addCallback(dojo.hitch(this, 
                                        function(resp) {
                                                this.cache[val] = resp;
                                        })
                                );
                                
                                c.addCallback(dojo.hitch(this, 
                                        '_handleResult'));
                        }
        },
        
        _handleResult : function(resp) {
                // ...
        }
});

Dependency Management and Building

I love jQuery, I do, but this is an area where it is sorely lacking. It offers little guidance on how to structure your codebase, how to ensure that everything you need is loaded, or how to build your code into production-ready files. Dojo really shines here, but again, it takes a bit of getting used to. The base dojo.js includes a ton of basic functionality, but I had to quickly learn to dojo.require() additional functionality as I needed it. In jQuery, you’d do this simply by adding another script tag to your HTML; dojo.require() basically does this for you programmatically, but checks to see that the required code hasn’t already been included first. This means each of your components can require exactly what it needs, and it’s perfectly safe to require the same thing more than once. The flip side of this is that if you forget to require something you need, or if you require it using the wrong name, it’s not always clear exactly where you made the mistake. Each file that will be dojo.require()’d begins with a dojo.provide() statement, telling the dependency management system that it has, indeed, found the right file. After that, the file can go on to dojo.require() its own dependencies, which Dojo loads before proceeding with the rest of the file’s code. The dojo.provide() method also sets up the object chain along the path; for example, if you dojo.provide(‘a.b.c.d’), you can then safely do a.b.c.d.e = { ... }. When it comes time to assemble your individual files into production-ready, combined, minified files, Dojo’s build system is able to parse dojo.require() statements and automatically include the appropriate files; with jQuery, this is a much more manual process that can be difficult to maintain. Creating a build wasn’t as straightforward as I’d hoped it would be, and I stumbed a lot along the way. It took a bit of doing to get all of the paths just right, and to figure out how to have a repeatable build process that we could roll up into our full release process. The payoff was big, though: I could keep my files organized how I wanted them, but only serve one file in production. The build system figured out the steps in between.

Organization, Inheritance and Abstraction

As I mentioned above, code organization has been a big issue for me with jQuery. I’ve developed some patterns that I use to keep my code sane, but plenty of other jQuery developers have not, which can make working with other people’s code rather painful. While it’s certainly possible to write procedural, disorganized code with Dojo, dojo.declare() provides a clear way to avoid it. I talked earlier about creating a namespace for my code and an associated directory. When it came time to start writing, I created individual files in that directory for each component on the page. Inside each file, I indicated which component the file was providing via dojo.provide(), loaded any dependencies via dojo.require(), and then created a class in the namespace using dojo.declare(). The dojo.declare() method takes three arguments: the name of the class you want to create (‘myNamespace.Thinger’), other classes you want to “mix in” to your new class (if any — this argument can be null, a single class, or an array of classes), and, lastly, an object that defines the class methods and properties. The result is a class that can be instantiated using new myNamespace.Thinger(); the object that’s created encapsulates all the behaviors and states associated with a particular Thinger, and you can have as many instances of Thinger as you want. The mixing in thing is huge, because it lets you have a class that incorporates methods defined in another class. For example, I created a class called myNamespace.Toggler that would show either the first item in a list or all of the items in a list; clicking on the first list item would toggle between the behaviors. Once the myNamespace.Toggler class was created, other classes could inherit its behavior simply by passing a reference to the myNamespace.Toggler class as the second argument of dojo.declare(). I was able to encapsulate the Toggler behavior in a reusable way, and keep the code for the classes that inherited the Toggler behavior nice and clean.

Event Management

I mentioned earlier that Dojo has a slightly different take on event binding than jQuery. I should also say that you can bind events to entire NodeLists (the result of dojo.query()) if you want, using .connect() (or convenience methods like .onclick(), etc.). However, if you want a reference to the connection for later use, dojo.connect() is your friend. I created a component using dojo.declare() that was responsible for managing user interaction with a star rating UI element. I used dojo.connect() to hook up some mouseover/mouseout behaviors to the element, and stored the returned connections as properties of the component. When the element was clicked, I registered the rating, and I wanted the mouseover/mouseout behaviors to go away; eliminating them was simply a matter of dojo.disconnect()-ing the stored connections.

Publish and Subscribe

With all of the XHR traffic occurring on the page, I wanted a way to shut it off if the user was inactive for a little while, but I didn’t want to write the code for handling that inside every component that used XHR. Dojo’s pub/sub tools offered the perfect answer. I created a new component whose sole purpose was to watch for inactivity; when it discovered inactivity, it would dojo.publish(‘/user/inactive’). Then, other components — the ones I’d already written, and ones I write in the future — could subscribe to the /user/inactive topic and react accordingly. Pub/sub is an excellent way to allow this sort of abstract communication between components. The component that publishes a “topic” doesn’t care who’s listening; the component that subscribes to a topic doesn’t care which component sent it. It’s another example of how Dojo leads you to think a bit differently about how you architect your applications — knowing about pub/sub can help you write much more loosely coupled code.

What I Missed from jQuery

jQuery’s event delegation-related methods .live() and .is() were hard to live without. There’s a reasonable way to mimic .is(), but no out-of-the-box replacement for .live() — you end up writing your event delegation yourself. Dojo does have dojo.behavior(), which automatically binds event handlers to elements that match a given selector as they’re added to the page; however, the event handlers are bound to individual elements, and there’s no way to provide a context to the selector that you pass to dojo.behavior(). This may be my noob-ness talking, or maybe I’m just used to the error messages I’d see when I did something wrong with jQuery, but I often found myself feeling that the error messages from Dojo were too cryptic. Worse, sometimes I’d do something wrong and it would seem to fail silently. I spent a lot more time in Firebug tracking down the errors of my ways. In general, the thing I really missed from jQuery was the “it just works” aspect of the library. I expect that with time I’ll feel that way about Dojo, but in the meantime there are definitely growing pains. I had to constantly remind myself that the way to solve a problem in Dojo might not be the same way I’d solve it in jQuery. More than once I rewrote a slew of code when I discovered some Dojo methodology or approach I hadn’t known about before.

Conclusion

Lest Rey worry that he never should have introduced me to Pete in the first place, fear not: I don’t expect to give up jQuery anytime soon. If anything, I’m excited to see how the library and the community mature and start answering some of the organization and dependency management questions I mentioned above. As a library, jQuery most definitely has its place; it has virtually no barriers to entry and it has helped usher in an era where it’s dead-simple to create rich, interactive websites. Deciding to use Dojo instead was something of a gamble. I had to convince the project lead that it was a good decision, which was challenging considering the popularity of the jQuery library. He asked lots of pointed questions about the maintainability of the code if I were to leave the project, and those questions were well deserved. If anything, though, I think that choosing Dojo has actually increased the maintainability of the code by presenting clear patterns for organization, abstraction, and dependency management. Did it take a while to come up to speed with Dojo? For sure. Will a jQuery developer off the street be able to jump right in to the code I wrote? Possibly not. At the end of the day, though, it is just JavaScript, and any skilled JavaScript developer should be able to find their way around. They’ll almost certainly find, given an hour or two, that the code I wrote is easier to follow than some of the jQuery code I’ve run into that doesn’t make use of good organizing principles. In the meantime, I hope to be working on the project for a while to come, and I expect the trouble I went through to come up to speed on Dojo will pay big dividends as the application I’m working on grows and matures.

Learn More

Filed under  //  dojo   jquery  
Comments (10)
Posted