O'Reilly

Using dojo/aspect

By on  

Simply put: the Dojo Toolkit has tools that other JavaScript toolkits don't. One of those tools includes Dojo 1.7's aspect, a module that allows developers to react to function calls by executing another function before or after that call. This aspect resource originates from Dojo's awesome connect mechanism. Let's check out how it works!

aspect Basics

The idea and value of aspect is that it allows you to be notified of function calls without needing to modify the original function's contents. You also don't need to implement pub/sub to know when methods are called. The basic setup looks like:

// Load the dojo/aspect dependency
require(["dojo/aspect"], function(aspect) {
	
	// Create an object and method to spy on
	var original = {
		someMethod: function(arg1, arg2) {
			console.warn("original.someMethod called: ", arg1, arg2);
			return "Hello " + arg1 + " " + arg2;
		}
	};
	
	// Use aspect.after ("before" also available)
	aspect.after(original, "someMethod", function(arg1, arg2) {
		console.warn("After method called with arguments: ", arg1, arg2);
	}, true);
	
	// ...and a while later, call the original method
	original.someMethod("David", "Walsh");
	
});

The first argument is the origin object, the second argument is the method to listen in on, and the third is the function to fire before or after, depending on which aspect method you call.

aspect.before

The before method will fire before the originally called method fires:

// Fire a function before the original
aspect.before(original, "someMethod", function(arg1, arg2) {
	console.warn("aspect.before: ", arg1, arg2);
});

// "aspect.before: ", David, Walsh
// "original.someMethod called:", David, Walsh

Since this method is fired before the original function call, the arguments passed to the original method are automatically received by the aspect method.

aspect.after

The after method fires after the original method fires, and accepts an extra parameter called receiveArguments, where you may direct the second method to receive the original method's arguments, or the return value of the original method.

receiveArguments = true

Passing the receiveArguments as true will pass the two arguments from the original function call to the aspect method:

// First scenario:  aspect.after that accepts the same arguments as the origin function
// because the fourth argument is passed as "true"
aspect.after(original, "someMethod", function(arg1, arg2) {
	console.warn("After method called with arguments: ", arg1, arg2);
}, true);

// Call the original method, see what happens!
original.someMethod("David", "Walsh");

// "original.someMethod called:", David, Walsh
// "aspect.after: ", David, Walsh

Removing the last argument instead allows the aspect method to receive the return value of the original function call:

// Second scenario: aspect.after that accepts the result of the original function call
aspect.after(original, "someMethod", function(arg1, arg2) {
	console.warn("aspect.after: ", arg1, arg2);
	
	// "arg2" will be "undefined" since only the return value is provided 
	
}); // No "true"

// Call the original method, see what happens!
original.someMethod("David", "Walsh");

// "original someMethod: David Walsh"
// "aspect.after: ", David Walsh

In most cases, aspect.after is what you will want to use. An object with a remove method will be returned, allowing you to remove the connection whenever you'd like.

aspect.around

What if you want to execute code both before and after the original method? Simple: use aspect.around. The around method allows you to call the original function when you want, within your listener:

// Call the around method, which gets one argument, the original method
aspect.around(original, "someMethod", function(originalMethod) {
	// Return a new method, which receives the same arguments as the original, just like "aspect.after"
	return function(arg1, arg2) {
		// Execute "before" functionality
		console.warn("before!", arg1, arg2);
		
		// Execute the original method as desired
		originalMethod.apply(this, arguments);
		
		// Execute "after" functioanlity
		console.warn("after!");
	};
});

// Call the original method, see what happens!
original.someMethod("David", "Walsh");

// "before!", David, Walsh
// "original someMethod: David Walsh"
// "after! David Walsh"

The idea behind aspect.around is very much like using this.inherited(arguments) within Dojo classes; you can make that call any time you'd like, so other code may run before or after.

Real Use of aspect

One realistic use of aspect relates to extending Dijit widgets, or building a Dijit layout that uses multiple widgets. Often you'll see something like:

// When the onChange event fires
aspect.after(this.dropdown, "onChange", function(value) {
	// ...fire an xhr request to get information about it
	dojo.xhrGet({
		url: "/data.php?value=" + value
	}).then(function(data) { 
		// Do something with it.
	});
}, true);

When the dropdown's value changes, an XHR request is fired to get information about that value.

Explaining aspect and the value of function-to-function connections can be difficult. I usually try a parent-to-child analogy that just further confuses people. Think of it this way: aspect allows you to know when other functions are called, without needing to modify the other function. This happens all over the Dijit UI framework, Brilliant!

O'Reilly Velocity Conference
Save 20% with discount code AFF20

Recent Features

  • An Interview with Eric Meyer

    Your early CSS books were instrumental in pushing my love for front end technologies. What was it about CSS that you fell in love with and drove you to write about it? At first blush, it was the simplicity of it as compared to the table-and-spacer...

  • Animated 3D Flipping Menu with CSS

    CSS animations aren't just for basic fades or sliding elements anymore -- CSS animations are capable of much more.  I've showed you how you can create an exploding logo (applied with JavaScript, but all animation is CSS), an animated Photo Stack, a sweet...

Incredible Demos

  • PHP / MooTools 1.2 Accordion Helper

    The MooTools Accordion plugin seems to be the plugin that people seem to have the most problems with. It's an awesome plugin, so I can see why so many people want to use it, but I think that may be part of the problem....

  • Image Manipulation with PHP and the GD Library

    Yeah, I'm a Photoshop wizard. I rock the selection tool. I crop like a farmer. I dominate the bucket tool. Hell, I even went as far as wielding the wizard wand selection tool once. ...OK I'm rubbish when it comes to Photoshop....

Discussion

  1. Christian Toma

    In the first code sample, the second argument of after shouldn’t it be a string ? The identifier “someMethod” does not exist

  2. Aspects are a powerful paradigm.
    One real example would be to test if the user is logged before execute some action.
    For a RIA app with many actions calling the server this can be easily solved with aspect.
    Great !!!

  3. In the first code block, you wrote original.someMethod("David Walsh");
    Ehrm.. hinting something about ", " to seperate things a bit.

  4. From what I’ve heard, it seems that a common tenet within aspect-oriented programming is to use the “least powerful” method you need to get the job done. aspect.around is significantly heavier than even connecting both before and after advice. Therefore, you should use aspect.around only when you truly need full control over if and when to call the original function. Otherwise, resist the urge – it may be tempting to overuse, due to its conceptual similarity to declare’s this.inherited.

  5. shawn

    Has anyone hoisted the aspect primitives of dojo into a framework to handle cross-cutting concerns? An aspect I like to apply to code is auto-tracing – e.g. logging info at method entry and exit without polluting the actual methods themselves. Maybe dojo can do this in a declarative fashion?

    For example, something like the interception behavior built-in to libraries like Unity on the .NET platform.

  6. Aspect is really nice, but why not use events instead?
    I prefer to expose events on my modules.

    I see Aspect beeing a hack where there are no events to hook.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Open Files from Command Line on OS X

    I'm as much of a fan of application UIs as anyone else but I'm finding myself working more and more from the command line lately.  Much of that is becoming obsessed with media manipulation but I'm forcing myself to use less UIs so that I...

  • Get Stock Quotes From Command Line

    When I conned my way into my first professional programming gig, I didn't really think much about money -- just that I was getting my foot in the door.  But as my career has gone on, I've been more aware of money, investing, and retirement.  I've recently...

  • Geolocation API

    One interesting aspect of web development is geolocation; where is your user viewing your website from? You can base your language locale on that data or show certain products in your store based on the user's location. Let's examine how you can...

  • Create an Image Preview from a Video

    Visuals are everything when it comes to media.  When I'm trying to decide whether to watch a video on Netflix, it would be awesome to see a trailer of some kind, but alas that isn't available.  When I'm looking to download a video on my computer,...

  • New:  Webdesigner News!

    A new and exciting website has recently been launched for web designers and developers. You likely spend hours every morning browsing through hundreds of posts on your RSS feeds, hoping to stumble across relevant stories. Webdesigner News was built to provide web designers and developers with...