The Group Class in MooTools
The MooTools Group class is for grouping a collection of objects and calling a function when all of those objects have fired a particular event. For instance, you could group an array of Element objects and fire an event when all of them have been clicked. Or, you could group a collection of Ajax request objects and fire an event when all have completed. (Both of these examples are straight from the MooTools 1.2 Beta docs for Plugins/Group).
How is this useful? Well, it's a convenience for when events depend on each others' completion but occur asynchronously, either by Ajax or through use of timers. Let's look at an example of how to handle dependent events without grouping first, to see where there is room for improvement:
// Flags to keep track of the request status
var firstRequestIsComplete = false;
var secondRequestIsComplete = false;
window.addEvent('domready', function() {
var firstRequest = new Request({
url: 'example-1.html',
onSuccess: function() {
firstRequestIsComplete = true;
if (secondRequestIsComplete) {
console.log('Both requests are complete.');
}
}
}).send(); // Fire it
var secondRequest = new Request({
url: 'example-1.html',
onSuccess: function() {
secondRequestIsComplete = true;
if (firstRequestIsComplete) {
console.log('Both requests are complete.');
}
}
}).send(); // Fire it
});
Obviously, this isn't a stellar solution. Having to keep track of which Ajax requests have already fired using flags is a hassle, and this example only uses two requests. Say you had 10 different requests and you wanted to fire an event once all of them had completed. You'd need to store flags for which request had fired and which hadn't, perhaps in a hash table, and write a function that checks if all of the requests had fired or not. Then, for the onSuccess event handler of each request, you'd need to set a condition that calls the function to check if all had succeeded, and performs your desired action if so (in the above example, logging to the console).
What a hassle! All of this checking and logic duplicated in each request; there must be a better way.
Grouping Ajax Requests in MooTools
Ajax by nickwheeleroz As it turns out, there is a better way, thanks to MooTools. Let's see how this code is cleaned up by the use of grouping with MooTools' Group class:
window.addEvent('domready', function() {
var firstRequest = new Request({url: 'example-1.html'});
var secondRequest = new Request({url: 'example-1.html'});
var ajaxRequests = new Group(firstRequest, secondRequest);
ajaxRequests.addEvent('onSuccess', function() {
console.log('Both requests are complete.');
});
// Fire requests
firstRequest.send();
secondRequest.send();
});
All right! Now we've removed the duplicate logging code and onSuccess events, which were previously declared in two places. This code has the exact same effect as the previous implementation, but we're following the "write once" principle.
You could make it even more concise using chaining, if that's your thing:
window.addEvent('domready', function() {
new Group(new Request({url: 'example-1.html'}),
new Request({url: 'example-1.html'}))
.addEvent('onSuccess', function() {
console.log('Both requests are complete.');
}).instances.each(function(instance) {
instance.send();
});
});
To me, the chaining isn't necessary here and iterating over the instances array stored in the group object probably isn't the best idea, since it's exposing the inner workings of the class by direct reference, rather than through the API. So, this example is only shown for the purposes of demonstrating how you can chain functions with MooTools, if you so desire.
Caveats and Gotchas with MooTools Groups
There are a few crucial gotchas that will hang you up if you aren't aware of them. First, the API documentation for MooTools 1.2beta is not accurate. It may be accurate for 1.1, but it certainly isn't for 1.2. They show an example of grouping using the Ajax class which was renamed Request in MooTools 1.2. The docs call the request() method to initiate an Ajax request but in actuality, it is the send() method. Can you spot any other inaccuracies in the example provided by the docs?
// From http://docs12b.mootools.net/Plugins/Group
//
// NOTE: This code will not work for MooTools 1.2.
// Shown for educational purposes only.
var xhr1 = new Ajax('data.js', {evalScript: true});
var xhr2 = new Ajax('abstraction.js', {evalScript: true});
var xhr3 = new Ajax('template.js', {evalScript: true});
var group = new Group(xhr1, xhr2, xhr3);
group.addEvent('complete', function(){
alert('All Scripts loaded');
});
xhr1.request();
xhr2.request();
xhr3.request();
A few other problems with the example: It shows the onComplete event instead of onSuccess, and the arguments passed to the Ajax constructor are incorrect as well. Finally, it shows the event to monitor without the 'on-' prefix. Given the plethora of inaccuracies in the docs, I believe these nuances warrant further discussion. Let's look at each of these in turn.
The Ajax class was renamed to Request in MooTools 1.2 and the API was significantly overhauled. Instead of calling request() to initiate the request, now you must send() it. Now, the constructor takes an options object which has properties for all of the arguments, such as url. In the example given, the URL is passed as the first argument, but in the current code you provide it as a property of the options object instead:
// This code demonstrates the differences in how to make
// Ajax requests between MooTools 1.1 and MooTools 1.2:
// Old way
new Ajax('path/to/your/data', {
// options go here
}).request(); // fire the request
// New way
new Request({
url: 'path/to/your/data'
// options go here
}).send(); // fire the request
As you can see, the URL is passed in via a property of the options object, rather than as the first argument of the constructor. I prefer this, as the MooTools API has been made much more consistent in 1.2 with most classes simply taking an options object, rather than a variety of parameters.
Another problem with the example provided in the docs is that it shows the onComplete event when this has been changed to onSuccess. And, to make matters worse, the docs exclude the 'on-' prefix in their example. Compare the right and wrong ways to monitor group events in MooTools 1.2:
// Wrong way (as shown in docs)
group.addEvent('complete', function(){
alert('All Scripts loaded');
});
// Right way
group.addEvent('onSuccess', function(){
alert('All Scripts loaded');
});
This is kind of odd, I'll admit, and inconsistent with the addEvent() API. Usually, you register events without the 'on-' prefix. But, for some reason, when you monitor an event in the group, you must use the 'on-' prefix. I discovered this after 20 minutes fruitlessly searching the web and stepping through my code, so hopefully this gem of information will save others that same trouble.
Testing MooTools Ajax Locally
Something crucial to be aware of when testing these examples is that
you must serve the response to the Ajax request from a web server. If you access the page through the file:// protocol instead of http:// protocol, certain headers will not be present and MooTools won't fire the onSuccess event.
There's actually a funny story about this: At my old job, there was a contest to build an Ajax widget using various JavaScript frameworks. The MooTools team got hung up because they were testing through the file:// protocol (without a web server, just by opening the file directly with a web browser) and the Ajax requests were never firing their onComplete event handlers (this was back in MooTools 1.1 where it was called onComplete instead of onSuccess). Well, what's funny about it is that the Ajax request would fire onFailure, but it called the event handler with the Ajax response! The team building the widget actually got it working fine but it was "failing" every time-- the Ajax callback function was registered to the onFailure event.
So, the moral of the story is, try to avoid hacky workaround like this and serve up the Ajax responses the right way, through a web server.
Besides Ajax, what are some other uses of groups you can think of? Post your ideas in the comments and they may become the basis of future articles.
The MooTools Group class is for grouping a collection of objects and calling a function when all of those objects have fired a particular event. For instance, you could group an array of Element objects and fire an event when all of them have been clicked. Or, you could group a collection of Ajax request objects and fire an event when all have completed. (Both of these examples are straight from the MooTools 1.2 Beta docs for Plugins/Group).

How is this useful? Well, it's a convenience for when events depend on each others' completion but occur asynchronously, either by Ajax or through use of timers. Let's look at an example of how to handle dependent events without grouping first, to see where there is room for improvement:
// Flags to keep track of the request status var firstRequestIsComplete = false; var secondRequestIsComplete = false; window.addEvent('domready', function() { var firstRequest = new Request({ url: 'example-1.html', onSuccess: function() { firstRequestIsComplete = true; if (secondRequestIsComplete) { console.log('Both requests are complete.'); } } }).send(); // Fire it var secondRequest = new Request({ url: 'example-1.html', onSuccess: function() { secondRequestIsComplete = true; if (firstRequestIsComplete) { console.log('Both requests are complete.'); } } }).send(); // Fire it });
Obviously, this isn't a stellar solution. Having to keep track of which Ajax requests have already fired using flags is a hassle, and this example only uses two requests. Say you had 10 different requests and you wanted to fire an event once all of them had completed. You'd need to store flags for which request had fired and which hadn't, perhaps in a hash table, and write a function that checks if all of the requests had fired or not. Then, for the onSuccess event handler of each request, you'd need to set a condition that calls the function to check if all had succeeded, and performs your desired action if so (in the above example, logging to the console).
What a hassle! All of this checking and logic duplicated in each request; there must be a better way.
Grouping Ajax Requests in MooTools

As it turns out, there is a better way, thanks to MooTools. Let's see how this code is cleaned up by the use of grouping with MooTools' Group class:
window.addEvent('domready', function() { var firstRequest = new Request({url: 'example-1.html'}); var secondRequest = new Request({url: 'example-1.html'}); var ajaxRequests = new Group(firstRequest, secondRequest); ajaxRequests.addEvent('onSuccess', function() { console.log('Both requests are complete.'); }); // Fire requests firstRequest.send(); secondRequest.send(); });
All right! Now we've removed the duplicate logging code and onSuccess events, which were previously declared in two places. This code has the exact same effect as the previous implementation, but we're following the "write once" principle.
You could make it even more concise using chaining, if that's your thing:
window.addEvent('domready', function() { new Group(new Request({url: 'example-1.html'}), new Request({url: 'example-1.html'})) .addEvent('onSuccess', function() { console.log('Both requests are complete.'); }).instances.each(function(instance) { instance.send(); }); });
To me, the chaining isn't necessary here and iterating over the instances array stored in the group object probably isn't the best idea, since it's exposing the inner workings of the class by direct reference, rather than through the API. So, this example is only shown for the purposes of demonstrating how you can chain functions with MooTools, if you so desire.
Caveats and Gotchas with MooTools Groups
There are a few crucial gotchas that will hang you up if you aren't aware of them. First, the API documentation for MooTools 1.2beta is not accurate. It may be accurate for 1.1, but it certainly isn't for 1.2. They show an example of grouping using the Ajax class which was renamed Request in MooTools 1.2. The docs call the request() method to initiate an Ajax request but in actuality, it is the send() method. Can you spot any other inaccuracies in the example provided by the docs?
// From http://docs12b.mootools.net/Plugins/Group // // NOTE: This code will not work for MooTools 1.2. // Shown for educational purposes only. var xhr1 = new Ajax('data.js', {evalScript: true}); var xhr2 = new Ajax('abstraction.js', {evalScript: true}); var xhr3 = new Ajax('template.js', {evalScript: true}); var group = new Group(xhr1, xhr2, xhr3); group.addEvent('complete', function(){ alert('All Scripts loaded'); }); xhr1.request(); xhr2.request(); xhr3.request();
A few other problems with the example: It shows the onComplete event instead of onSuccess, and the arguments passed to the Ajax constructor are incorrect as well. Finally, it shows the event to monitor without the 'on-' prefix. Given the plethora of inaccuracies in the docs, I believe these nuances warrant further discussion. Let's look at each of these in turn.
The Ajax class was renamed to Request in MooTools 1.2 and the API was significantly overhauled. Instead of calling request() to initiate the request, now you must send() it. Now, the constructor takes an options object which has properties for all of the arguments, such as url. In the example given, the URL is passed as the first argument, but in the current code you provide it as a property of the options object instead:
// This code demonstrates the differences in how to make // Ajax requests between MooTools 1.1 and MooTools 1.2: // Old way new Ajax('path/to/your/data', { // options go here }).request(); // fire the request // New way new Request({ url: 'path/to/your/data' // options go here }).send(); // fire the request
As you can see, the URL is passed in via a property of the options object, rather than as the first argument of the constructor. I prefer this, as the MooTools API has been made much more consistent in 1.2 with most classes simply taking an options object, rather than a variety of parameters.
Another problem with the example provided in the docs is that it shows the onComplete event when this has been changed to onSuccess. And, to make matters worse, the docs exclude the 'on-' prefix in their example. Compare the right and wrong ways to monitor group events in MooTools 1.2:
// Wrong way (as shown in docs) group.addEvent('complete', function(){ alert('All Scripts loaded'); }); // Right way group.addEvent('onSuccess', function(){ alert('All Scripts loaded'); });
This is kind of odd, I'll admit, and inconsistent with the addEvent() API. Usually, you register events without the 'on-' prefix. But, for some reason, when you monitor an event in the group, you must use the 'on-' prefix. I discovered this after 20 minutes fruitlessly searching the web and stepping through my code, so hopefully this gem of information will save others that same trouble.
Testing MooTools Ajax Locally
Something crucial to be aware of when testing these examples is that you must serve the response to the Ajax request from a web server. If you access the page through the file:// protocol instead of http:// protocol, certain headers will not be present and MooTools won't fire the onSuccess event.
There's actually a funny story about this: At my old job, there was a contest to build an Ajax widget using various JavaScript frameworks. The MooTools team got hung up because they were testing through the file:// protocol (without a web server, just by opening the file directly with a web browser) and the Ajax requests were never firing their onComplete event handlers (this was back in MooTools 1.1 where it was called onComplete instead of onSuccess). Well, what's funny about it is that the Ajax request would fire onFailure, but it called the event handler with the Ajax response! The team building the widget actually got it working fine but it was "failing" every time-- the Ajax callback function was registered to the onFailure event.
So, the moral of the story is, try to avoid hacky workaround like this and serve up the Ajax responses the right way, through a web server.
Besides Ajax, what are some other uses of groups you can think of? Post your ideas in the comments and they may become the basis of future articles.
2 Comments:
Hello,
first, pls forgive me for not posting directly about your work, which is very interesting.
As you know, use of copy/paste is mandatory in these cases. Pb is, in doing so, your code is "bulleted", and keeps the line numbers. Very annoying. There are ways around but if you could "ease the pain", it'd be wonderful.
@roselavy: Thanks for pointing this out. I've re-published the post with options on the code to view as plaintext and copy to the clipboard now.
I'm using a JavaScript library called the dp.SyntaxHighlighter. It's quite good (in my opinion) but including line numbers in the JS is definitely a problem! But I republished it with different settings (previously it had the 'nocontrols' flag so wasn't showing the buttons to copy, etc), so hopefully this will be of help to you.
You are right to say that copy/paste is necessary, though I know of some who have a different attitude-- my friend Chris has been known to say "Copy and paste is the root of all evils."
For more info on the dp.SyntaxHighlighter, check here: http://www.dreamprojections.com/syntaxhighlighter/?ref=about
Post a Comment
Subscribe to Post Comments [Atom]
<< Home