Dojo FAQ: What is the difference between dojo/on and dojo/aspect?

By on March 26, 2014 10:01 am

DojoFAQ

dojo/on and dojo/aspect are two APIs that appear to do similar jobs but actually serve two different purposes. dojo/on is used to setup event listeners for DOM nodes and event-emitting objects. dojo/aspect provides Aspect Oriented Programming facilities so functionality can be run before, after, or around a method call and can even manipulate the input to the original method or the original method’s output.

dojo/on

dojo/on is the general purpose event module and can be used to listen for events on DOM nodes and event-emitting objects. The most basic use is to set up an event listener on an element directly or to use event delegation.

require([
	'dojo/on',
	'dojo/query'
], function (on) {
	var actionButton = document.getElementById('actionButton');
	// add event listener directly to a DOM Node
	on(actionButton, 'click', function () {
		console.log('button clicked');
	});

	// use event delegation to listen for events
	on(document, '.button:click', function () {
		console.log('button clicked');
	});
});

dojo/on can also be used to set up listeners for events emitted from objects that inherit from dojo/Evented. These objects gain an emit method, which can be used to trigger a custom event.

require([
	'dojo/_base/declare',
	'dojo/Evented',
	'dojo/on'
], function (declare, Evented, on) {
	var MyClass = declare(Evented, {
		run: function () {
			this.emit('custom-event', { name: 'world' });
		}
	});

	var c = new MyClass();

	on(c, 'custom-event', function (data) {
		console.log('hello, ' + data.name);
	});

	c.run();
});

dojo/aspect

On the other hand, dojo/aspect can be used in a number of ways to run code before, after, or around a method call. For example, if we have a rand object that has a method getNumber which returns a random number in a given range, we can use aspect.after to call a new method after the original method is executed and change the return value to be a little less random:

var div = document.querySelector('#result'),
    after = document.querySelector('#after');
require([
    'dojo/aspect'
], function (aspect) {
    var rand = {
        getNumber: function (min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    };

    aspect.after(rand, 'getNumber', function (value) {
        console.log('actual result ' + value);
        return 4;
    });

    console.log('returned result ' + rand.getNumber(3, 10));
});

Similarly, we could use aspect.before to execute code before getNumber is called and return an array, changing the arguments that are passed to the original function:

require([
    'dojo/aspect'
], function (aspect) {
    var rand = {
        getNumber: function (min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    };

    aspect.before(rand, 'getNumber', function (min, max) {
        console.log('original arguments: ' + min + ', ' + max);
        return [6, 16];
    });

    console.log('returned result: ' + rand.getNumber(20, 30));
});

Finally, aspect.around can be used to return a function to replace the original method. This new method can then conditionally call the original method if necessary:

require([
    'dojo/aspect'
], function (aspect) {
    var rand = {
        getNumber: function (min, max) {
            console.log('original method called');
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    };

    aspect.around(rand, 'getNumber', function (origMethod) {
        return function (min, max) {
            if (min === max) {
                console.log('not calling original method');
                return min;
            }
            // only call the original method if min !== max
            return origMethod(min, max);
        };
    });

    console.log('returned result: ' + rand.getNumber(20, 20));
});

As you can see, dojo/on is a useful utility for listening for events from the DOM or event-emitting objects, while dojo/aspect is useful for injecting code before, after, or around the original method call.