AJAX In Action [173]
“constant” parameter array to send with every Ajax request. Also recall that because we passed this as a component argument, the DoubleCombo object is obligated to implement the implied contract with the net.ContentLoader object we discussed earlier. That is, we must implement an ajaxUpdate() and a handleError() method. We’ll get to that in a bit, but first let’s look at the last line of our constructor:
this.initializeBehavior();
Finally our constructor is doing something that looks like behavior. Yes, the moment we’ve all been waiting for: the behavior implementation. Everything we’ll do from here on out is directly related to providing double-combo functionality. So without further ado, let’s take a look at this method along with all the other DoubleCombo methods that will be required. Thanks to all of the infrastructure we’ve put in place, our task is far from daunting at this point. Keep in mind that all the methods that appear throughout the rest of the example are assumed to be embedded within a prototype literal object, exactly as we did for the net.ContentLoader implementation.
Licensed to jonathan zheng 356 CHAPTER 9 Dynamic double combo DoubleCombo.prototype = { // all of the methods…. }; So, let’s peek under the hood. First, the initializeBehavior() method is shown here: initializeBehavior: function() { var oThis = this; this.master.onchange = function() { oThis.masterComboChanged(); }; }, Short and sweet. This method puts an onchange event handler on the master select element (formerly done in the HTML markup itself). When triggered, the event handler invokes another method on our object, masterComboChanged(): masterComboChanged: function() { var query = this.master.options[ this.master.selectedIndex].value; this.ajaxHelper.sendRequest( 'q=' + query ); }, Wow, also short and sweet. All this method has to do is create a request parameter and send our Ajax request. Since the Ajax-specific work has been factored out into another object, this is a single line of code. Recall that sendRequest() will create and send an XMLHttpRequest, then route the response back to our ajaxUpdate() method. So let’s write that: ajaxUpdate: function(request) { var slaveOptions = this.createOptions( request.responseXML.documentElement); this.slave.length = 0; Clear any existing options for ( var i = 0 ; i < slaveOptions.length ; i++ ) try{ this.slave.add(slaveOptions[i],null); Populate }catch (e){ new options this.slave.add(slaveOptions[i],-1); } }, This method takes the response XML from the request object and passes it to a method named createOptions(), which creates our slave select’s option elements. The method then simply clears and repopulates the slave select element. The createOptions() method, although not part of any public contract, is a helper method that makes the code cleaner and more readable. Its implementation, along with another helper method, getElementContent(), is shown in listing 9.13. Licensed to jonathan zheng Refactoring 357 Listing 9.13 Combo population methods createOptions: function(ajaxResponse) { var newOptions = []; var entries = ajaxResponse.getElementsByTagName('entry'); for ( var i = 0 ; i < entries.length ; i++ ) { var text = this.getElementContent(entries[i], 'optionText'); var value = this.getElementContent(entries[i], 'optionValue'); newOptions.push( new Option( text, value ) ); } return newOptions; }, getElementContent: function(element,tagName) { var childElement = element.getElementsByTagName(tagName)[0]; return (childElement.text != undefined) ? childElement.text : childElement.textContent; }, These methods perform the hard work of actually fetching values from the XML response document, and creating options objects from them. To recap, the XML structure of the response is as follows: