Neuron Workout Solutions #4
Welcome to this fourth installment of Neuron Workout Solutions™, a series that answers the questions and challenges at the end of many chapters in the book. I know this one’s been a tad longer than the others in coming, so today we’ll address the challenges closing two chapters: chapters 8 (Form Management) and 9 (Ajax Has Never Been So Easy).
How would you implement an equivalent of Field.Observer using PeriodicalExecuterdirectly? What about Form.Observer?
As a matter of fact, Field.Observer and Form.Observer are based on PeriodicalExecuter, through some sort of inheritance. But if we were to just use PeriodicalExecuter, how would we go about it? We need to determine what to do at a regular interval. Basically, we must:
- Look up the field’s value
- Compare that to the latest known value
- If the values are different, we trigger the callback!
- Obviously, we would also store the current value then, for comparison the next time around.
Oh, and when we start the whole observation thing, we store the field’s value as the latest known value, so we don’t trigger the callback the first time around…
Here’s how it could be implemented:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var MyFieldObserver = Class.create({ initialize: function(element, interval, callback) { this.element = $(element); this.latestValue = $F(this.element); var that = this; new PeriodicalExecuter(function() { var newValue = $F(that.element); if (newValue != that.latestValue) { that.latestValue = newValue; callback(that.element, newValue); } }, interval); } }); |
We’re creating a class here only so we can associate storage with each call (through the this reference). It could also have been done through a module pattern hiding a global hash, but that would just be re-inventing the wheel.
Another item of interest here is that I chose, for once, not to bind the anonymous function passed to PeriodicalExecuter, but to leverage lexical closure with the usual var that = this trick. Remember that passing function references loses their binding, and this is not just a regular variable. If we wanted to be able to use it inside the anonymous function (e.g. var newValue = $F(this.element); or if (newValue != this.latestValue)), we would have needed to bind this anonymous functions explicitly, thereby doubling function calls. So we went with lexical closure instead, just for kicks.
As for Form.Observer, we would just need to use the form-wide equivalent of Field#getValue. If you read the chapter carefully, you understand this is essentially what Form#serialize does. So we would just have to replace our $F(this.element) (and its that-based equivalent) with this.element.serialize() (or that…). That’s it!
Write a method that takes all the radio buttons with a given field name and toggles their availability (enables or disables them, depending on their state).
The trick here is mostly about the selection of the proper elements; toggling is fairly simple. That’s a perfect fit for the Form#getInputs call! Here’s an attempt:
1 2 3 4 5 | function toggleRadios(form, radioName) { $(form).getInputs('radio', radioName).each(function(radio) { radio[radio.disabled ? 'enable' : 'disable'](); }); } |
One more case where the [] operator for property/method selection is a great fit for a simple, ternary-based call switch.
That’s really all there is to it. For styling purposes though, we might also want to associate disabled status with a “disabled” class, to make it easier to obtain a given aspect across browsers. We just need an extra line:
1 2 3 4 5 6 | function toggleRadios(form, radioName) { $(form).getInputs('radio', radioName).each(function(radio) { radio[(radio.disabled ? 'remove' : 'add') + 'ClassName']('disabled'); radio[radio.disabled ? 'enable' : 'disable'](); }); } |
And there it is!
What’s the best way to return a document fragment on which scripting must be applied?
Sam’s way: return the fragment with an inline script appended to it, and fetch it through a Ajax.Updater with its evalScripts option set to true. As Sam is quick to point out, this has the added benefit of putting the scripting close to the fragment it’s operating upon, instead of coupling static JS files to every possible situation they’ll have to face, making them unnecessarily bloated.
Here’s an example:
new Ajax.Updater('comeContainer', '/my_cool_url', { evalScripts: true });
And the fetched fragment could be something like this:
<li id="item_42">Pick up dry cleaning</li> <script type="text/javascript"> $('item_42').highlight(); </script>
Remember that on their own, <script> elements loaded through Ajax will not be executed by the browser. What Prototype does when the evalScripts option is on, is extract the script elements’ contents, then strip these elements from the fragment’s code before inserting it into the DOM, and finally execute the script part a tiny bit of time later (thanks to a defer() call, see the book and earlier Neuron Workouts for more details on defer()).
Say you use Ajax to add items to a task list. Each task item contains a checkbox, and you need to react to that checkbox being clicked to put the item in the “pending” or “done” list. The easiest solution does not use inline <script> tags in your Ajax responses and is more efficient to boot. What is it?
Ah ha. Event delegation, my friend. Scripting Ajax-loaded elements is always a nightmare, because such elements get lost (they are replaced by updated contents) without, most of the time, our releasing their dedicated event handlers, and they must get new handlers after every load. So clearly, this bind-to-the-elements approach blows on Ajax-loaded contents. Whenever we can avoid it we should.
So when can we avoid it?
When the events we want to observe bubble. Then we can just observe once on the container of the loaded contents, and use methods such as Event#findElement to make sure the events we get triggered on the elements we’re interested in. Imagine we’re into reacting to links with a deleter class inside Ajax-loaded contents. Say this content is always loaded inside a container with id="items". We could go like the following, done just once when the initial page’s DOM is loaded:
$('items').observe(function(e) { var activator = e.findElement('a.deleter'); if (!activator) return; e.stop(); // Your processing here… });
I use this pattern all the time; it’s way more efficient than per-item observation, it doesn’t leak memory like a sieve, it’s faster to initialize, it’s just better.
Now, most events bubble, but sometimes you’ll hit a snag: they don’t. The most common pitfalls here are the focus and blur events. If you need to observe such events on Ajax-loaded form fields, you’ll have to observe the loaded fields individually; if you’re going clean, you’ll have to “unobserve” all of these prior to replacing this contents with another, to avoid memory leaks. Prototype’s recent simplified syntaxes to stopObserving are meant to help with that, as will upcoming updates to Prototype using “standardized” custom events…
Create a sign-up form that reacts to the user typing a login by live-checking whether that login is available; if it’s not, it styles the field accordingly, displays a message, and disables the submit button. If it is available, it does the opposite. Use a simple server-side script in whatever language you like, with a static list of already-taken logins, to help you test it. Also make sure the lookup interval is not so small it hogs the browser.
OK, so let’s take the tiny decisions:
- We won’t style valid logins in any special way; we’ll just style the
<input type="text"…/>element with an extratakenclass if the login is already taken. - Our server-side script will just use HTTP response codes to indicate whether the login is available or not: 200 means we’re good to go, 403 means the login is already taken.
- Our parameter for the server-side script will be named
login.
So let’s start with the HTML fragment for the sign-up form:
<form id="signUpForm" action="/users/new" method="post"…> … <input type="text" id="edtLogin" name="login" value="" /> … <p><input type="submit" id="btnSubmit" value="Sign up!" /></p> </form>
Now here’s our client-side script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function bindLoginCheck() { var submitter = $('btnSubmit'); new Field.Observer('edtLogin', 0.3, function(field, value) { var req = new Ajax.Request('checklogin.php', { method: 'get', parameters: { login: value }, onComplete: function() { field[(req.success() ? 'remove' : 'add') + 'ClassName']('taken'); submitter[req.success() ? 'show' : 'hide'](); } }); }); } // bindLoginCheck document.observe('dom:loaded', function() { bindLoginCheck(); }); |
We again use ternaries to quickly select which methods to use (addClassName vs. removeClassName, hide vs. show…). The success() method on Ajax.Request objects is based on the HTTP response’s status codes: it is deemed successful if it’s in the 200-299 range, as mandated per the HTTP specs.
Now on to the server-side script. Here is a PHP example, the checklogin.php page:
1 2 3 4 5 6 7 8 | <?php $LOGINS = array('savetheclocktower', 'doudou', 'mislav', 'tobie', 'tdd'); if (in_array($_GET['login'], $LOGINS)) { header('HTTP/1.1 403 Login is already taken'); } else { header('HTTP/1.1 200 Login is available'); } ?> |
Here’s a self-sufficient (HTTP server and all) Ruby script for testing, much like I used in the book, to test the whole thing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #! /usr/bin/env ruby require 'cgi' require 'webrick' include WEBrick server = HTTPServer.new(:Port => 8042) server.mount('/', HTTPServlet::FileHandler, '.') LOGINS = %w(savetheclocktower doudou mislav tobie tdd) server.mount_proc('/checklogin') do |request, response| response.status = LOGINS.include?(request.query['login']) ? 403 : 200 end trap('INT') { server.shutdown } server.start |
There, your choice of server layer
I would have added a JavaEE example, but I said “simple server script,” didn’t I?
And that’s a wrap!
I’ll look forward to any questions, comments or wild guesses you may have. And stay tuned for the fifth installment!
No comments yet. Be the first.
Leave a reply


